spoolss: use nstring_array in spoolss_EnumPrinterKey.
[Samba.git] / source3 / smbd / chgpasswd.c
blobccab71c0a2b388034ec577e1ff6ae5f197b5e813
1 /*
2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2004
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 /* These comments regard the code to change the user's unix password: */
23 /* fork a child process to exec passwd and write to its
24 * tty to change a users password. This is running as the
25 * user who is attempting to change the password.
29 * This code was copied/borrowed and stolen from various sources.
30 * The primary source was the poppasswd.c from the authors of POPMail. This software
31 * was included as a client to change passwords using the 'passwd' program
32 * on the remote machine.
34 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
35 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
36 * and rights to modify, distribute or incorporate this change to the CAP suite or
37 * using it for any other reason are granted, so long as this disclaimer is left intact.
41 This code was hacked considerably for inclusion in Samba, primarily
42 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
43 of the "password chat" option, which allows the easy runtime
44 specification of the expected sequence of events to change a
45 password.
48 #include "includes.h"
50 static NTSTATUS check_oem_password(const char *user,
51 uchar password_encrypted_with_lm_hash[516],
52 const uchar old_lm_hash_encrypted[16],
53 uchar password_encrypted_with_nt_hash[516],
54 const uchar old_nt_hash_encrypted[16],
55 struct samu **hnd,
56 char **pp_new_passwd);
58 #if ALLOW_CHANGE_PASSWORD
60 static int findpty(char **slave)
62 int master = -1;
63 char *line = NULL;
64 SMB_STRUCT_DIR *dirp = NULL;
65 const char *dpname;
67 *slave = NULL;
69 #if defined(HAVE_GRANTPT)
70 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
71 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
72 grantpt(master);
73 unlockpt(master);
74 line = (char *)ptsname(master);
75 if (line) {
76 *slave = SMB_STRDUP(line);
79 if (*slave == NULL) {
80 DEBUG(0,
81 ("findpty: Unable to create master/slave pty pair.\n"));
82 /* Stop fd leak on error. */
83 close(master);
84 return -1;
85 } else {
86 DEBUG(10,
87 ("findpty: Allocated slave pty %s\n", *slave));
88 return (master);
91 #endif /* HAVE_GRANTPT */
93 line = SMB_STRDUP("/dev/ptyXX");
94 if (!line) {
95 return (-1);
98 dirp = sys_opendir("/dev");
99 if (!dirp) {
100 SAFE_FREE(line);
101 return (-1);
104 while ((dpname = readdirname(dirp)) != NULL) {
105 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
106 DEBUG(3,
107 ("pty: try to open %s, line was %s\n", dpname,
108 line));
109 line[8] = dpname[3];
110 line[9] = dpname[4];
111 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
112 DEBUG(3, ("pty: opened %s\n", line));
113 line[5] = 't';
114 *slave = line;
115 sys_closedir(dirp);
116 return (master);
120 sys_closedir(dirp);
121 SAFE_FREE(line);
122 return (-1);
125 static int dochild(int master, const char *slavedev, const struct passwd *pass,
126 const char *passwordprogram, bool as_root)
128 int slave;
129 struct termios stermios;
130 gid_t gid;
131 uid_t uid;
132 char * const eptrs[1] = { NULL };
134 if (pass == NULL)
136 DEBUG(0,
137 ("dochild: user doesn't exist in the UNIX password database.\n"));
138 return False;
141 gid = pass->pw_gid;
142 uid = pass->pw_uid;
144 gain_root_privilege();
146 /* Start new session - gets rid of controlling terminal. */
147 if (setsid() < 0)
149 DEBUG(3,
150 ("Weirdness, couldn't let go of controlling terminal\n"));
151 return (False);
154 /* Open slave pty and acquire as new controlling terminal. */
155 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
157 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
158 return (False);
160 #if defined(TIOCSCTTY) && !defined(SUNOS5)
162 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
163 * see the discussion under
164 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
166 if (ioctl(slave, TIOCSCTTY, 0) < 0)
168 DEBUG(3, ("Error in ioctl call for slave pty\n"));
169 /* return(False); */
171 #elif defined(I_PUSH) && defined(I_FIND)
172 if (ioctl(slave, I_FIND, "ptem") == 0) {
173 ioctl(slave, I_PUSH, "ptem");
175 if (ioctl(slave, I_FIND, "ldterm") == 0) {
176 ioctl(slave, I_PUSH, "ldterm");
178 #endif
180 /* Close master. */
181 close(master);
183 /* Make slave stdin/out/err of child. */
185 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
187 DEBUG(3, ("Could not re-direct stdin\n"));
188 return (False);
190 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
192 DEBUG(3, ("Could not re-direct stdout\n"));
193 return (False);
195 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
197 DEBUG(3, ("Could not re-direct stderr\n"));
198 return (False);
200 if (slave > 2)
201 close(slave);
203 /* Set proper terminal attributes - no echo, canonical input processing,
204 no map NL to CR/NL on output. */
206 if (tcgetattr(0, &stermios) < 0)
208 DEBUG(3,
209 ("could not read default terminal attributes on pty\n"));
210 return (False);
212 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
213 stermios.c_lflag |= ICANON;
214 #ifdef ONLCR
215 stermios.c_oflag &= ~(ONLCR);
216 #endif
217 if (tcsetattr(0, TCSANOW, &stermios) < 0)
219 DEBUG(3, ("could not set attributes of pty\n"));
220 return (False);
223 /* make us completely into the right uid */
224 if (!as_root)
226 become_user_permanently(uid, gid);
229 DEBUG(10,
230 ("Invoking '%s' as password change program.\n",
231 passwordprogram));
233 /* execl() password-change application */
234 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
236 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
237 return (False);
239 return (True);
242 static int expect(int master, char *issue, char *expected)
244 char buffer[1024];
245 int attempts, timeout, nread;
246 size_t len;
247 bool match = False;
249 for (attempts = 0; attempts < 2; attempts++) {
250 NTSTATUS status;
251 if (!strequal(issue, ".")) {
252 if (lp_passwd_chat_debug())
253 DEBUG(100, ("expect: sending [%s]\n", issue));
255 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
256 DEBUG(2,("expect: (short) write returned %d\n",
257 (int)len ));
258 return False;
262 if (strequal(expected, "."))
263 return True;
265 /* Initial timeout. */
266 timeout = lp_passwd_chat_timeout() * 1000;
267 nread = 0;
268 buffer[nread] = 0;
270 while (True) {
271 status = read_socket_with_timeout(
272 master, buffer + nread, 1,
273 sizeof(buffer) - nread - 1,
274 timeout, &len);
276 if (!NT_STATUS_IS_OK(status)) {
277 break;
279 nread += len;
280 buffer[nread] = 0;
283 /* Eat leading/trailing whitespace before match. */
284 char *str = SMB_STRDUP(buffer);
285 if (!str) {
286 DEBUG(2,("expect: ENOMEM\n"));
287 return False;
289 trim_char(str, ' ', ' ');
291 if ((match = unix_wild_match(expected, str)) == True) {
292 /* Now data has started to return, lower timeout. */
293 timeout = lp_passwd_chat_timeout() * 100;
295 SAFE_FREE(str);
299 if (lp_passwd_chat_debug())
300 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
301 expected, buffer, match ? "yes" : "no" ));
303 if (match)
304 break;
306 if (!NT_STATUS_IS_OK(status)) {
307 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
308 return False;
312 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
313 return match;
316 static void pwd_sub(char *buf)
318 all_string_sub(buf, "\\n", "\n", 0);
319 all_string_sub(buf, "\\r", "\r", 0);
320 all_string_sub(buf, "\\s", " ", 0);
321 all_string_sub(buf, "\\t", "\t", 0);
324 static int talktochild(int master, const char *seq)
326 TALLOC_CTX *frame = talloc_stackframe();
327 int count = 0;
328 char *issue;
329 char *expected;
331 issue = talloc_strdup(frame, ".");
332 if (!issue) {
333 TALLOC_FREE(frame);
334 return false;
337 while (next_token_talloc(frame, &seq, &expected, NULL)) {
338 pwd_sub(expected);
339 count++;
341 if (!expect(master, issue, expected)) {
342 DEBUG(3, ("Response %d incorrect\n", count));
343 TALLOC_FREE(frame);
344 return false;
347 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
348 issue = talloc_strdup(frame, ".");
349 if (!issue) {
350 TALLOC_FREE(frame);
351 return false;
354 pwd_sub(issue);
357 if (!strequal(issue, ".")) {
358 /* we have one final issue to send */
359 expected = talloc_strdup(frame, ".");
360 if (!expected) {
361 TALLOC_FREE(frame);
362 return false;
364 if (!expect(master, issue, expected)) {
365 TALLOC_FREE(frame);
366 return False;
369 TALLOC_FREE(frame);
370 return (count > 0);
373 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
374 char *chatsequence, bool as_root)
376 char *slavedev = NULL;
377 int master;
378 pid_t pid, wpid;
379 int wstat;
380 bool chstat = False;
382 if (pass == NULL) {
383 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
384 return False;
387 /* allocate a pseudo-terminal device */
388 if ((master = findpty(&slavedev)) < 0) {
389 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
390 return (False);
394 * We need to temporarily stop CatchChild from eating
395 * SIGCLD signals as it also eats the exit status code. JRA.
398 CatchChildLeaveStatus();
400 if ((pid = sys_fork()) < 0) {
401 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
402 SAFE_FREE(slavedev);
403 close(master);
404 CatchChild();
405 return (False);
408 /* we now have a pty */
409 if (pid > 0) { /* This is the parent process */
410 /* Don't need this anymore in parent. */
411 SAFE_FREE(slavedev);
413 if ((chstat = talktochild(master, chatsequence)) == False) {
414 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
415 kill(pid, SIGKILL); /* be sure to end this process */
418 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
419 if (errno == EINTR) {
420 errno = 0;
421 continue;
423 break;
426 if (wpid < 0) {
427 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
428 close(master);
429 CatchChild();
430 return (False);
434 * Go back to ignoring children.
436 CatchChild();
438 close(master);
440 if (pid != wpid) {
441 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
442 return (False);
444 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
445 DEBUG(3, ("chat_with_program: The process exited with status %d \
446 while we were waiting\n", WEXITSTATUS(wstat)));
447 return (False);
449 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
450 else if (WIFSIGNALLED(wstat)) {
451 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
452 while we were waiting\n", WTERMSIG(wstat)));
453 return (False);
455 #endif
456 } else {
457 /* CHILD */
460 * Lose any elevated privileges.
462 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
463 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
465 /* make sure it doesn't freeze */
466 alarm(20);
468 if (as_root)
469 become_root();
471 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
472 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
473 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
475 if (as_root)
476 unbecome_root();
479 * The child should never return from dochild() ....
482 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
483 exit(1);
486 if (chstat)
487 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
488 (chstat ? "" : "un"), pass->pw_name));
489 return (chstat);
492 bool chgpasswd(const char *name, const struct passwd *pass,
493 const char *oldpass, const char *newpass, bool as_root)
495 char *passwordprogram = NULL;
496 char *chatsequence = NULL;
497 size_t i;
498 size_t len;
499 TALLOC_CTX *ctx = talloc_tos();
501 if (!oldpass) {
502 oldpass = "";
505 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
507 #ifdef DEBUG_PASSWORD
508 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
509 #endif
511 /* Take the passed information and test it for minimum criteria */
513 /* Password is same as old password */
514 if (strcmp(oldpass, newpass) == 0) {
515 /* don't allow same password */
516 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
517 return (False); /* inform the user */
521 * Check the old and new passwords don't contain any control
522 * characters.
525 len = strlen(oldpass);
526 for (i = 0; i < len; i++) {
527 if (iscntrl((int)oldpass[i])) {
528 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
529 return False;
533 len = strlen(newpass);
534 for (i = 0; i < len; i++) {
535 if (iscntrl((int)newpass[i])) {
536 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
537 return False;
541 #ifdef WITH_PAM
542 if (lp_pam_password_change()) {
543 bool ret;
544 #ifdef HAVE_SETLOCALE
545 const char *prevlocale = setlocale(LC_ALL, "C");
546 #endif
548 if (as_root)
549 become_root();
551 if (pass) {
552 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
553 } else {
554 ret = smb_pam_passchange(name, oldpass, newpass);
557 if (as_root)
558 unbecome_root();
560 #ifdef HAVE_SETLOCALE
561 setlocale(LC_ALL, prevlocale);
562 #endif
564 return ret;
566 #endif
568 /* A non-PAM password change just doen't make sense without a valid local user */
570 if (pass == NULL) {
571 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
572 return false;
575 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
576 if (!passwordprogram || !*passwordprogram) {
577 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
578 return false;
580 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
581 if (!chatsequence || !*chatsequence) {
582 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
583 return false;
586 if (as_root) {
587 /* The password program *must* contain the user name to work. Fail if not. */
588 if (strstr_m(passwordprogram, "%u") == NULL) {
589 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
590 the string %%u, and the given string %s does not.\n", passwordprogram ));
591 return false;
595 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
596 if (!passwordprogram) {
597 return false;
600 /* note that we do NOT substitute the %o and %n in the password program
601 as this would open up a security hole where the user could use
602 a new password containing shell escape characters */
604 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
605 if (!chatsequence) {
606 return false;
608 chatsequence = talloc_all_string_sub(ctx,
609 chatsequence,
610 "%o",
611 oldpass);
612 if (!chatsequence) {
613 return false;
615 chatsequence = talloc_all_string_sub(ctx,
616 chatsequence,
617 "%n",
618 newpass);
619 return chat_with_program(passwordprogram,
620 pass,
621 chatsequence,
622 as_root);
625 #else /* ALLOW_CHANGE_PASSWORD */
627 bool chgpasswd(const char *name, const struct passwd *pass,
628 const char *oldpass, const char *newpass, bool as_root)
630 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
631 return (False);
633 #endif /* ALLOW_CHANGE_PASSWORD */
635 /***********************************************************
636 Code to check the lanman hashed password.
637 ************************************************************/
639 bool check_lanman_password(char *user, uchar * pass1,
640 uchar * pass2, struct samu **hnd)
642 uchar unenc_new_pw[16];
643 uchar unenc_old_pw[16];
644 struct samu *sampass = NULL;
645 uint32 acct_ctrl;
646 const uint8 *lanman_pw;
647 bool ret;
649 if ( !(sampass = samu_new(NULL)) ) {
650 DEBUG(0, ("samu_new() failed!\n"));
651 return False;
654 become_root();
655 ret = pdb_getsampwnam(sampass, user);
656 unbecome_root();
658 if (ret == False) {
659 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
660 TALLOC_FREE(sampass);
661 return False;
664 acct_ctrl = pdb_get_acct_ctrl (sampass);
665 lanman_pw = pdb_get_lanman_passwd (sampass);
667 if (acct_ctrl & ACB_DISABLED) {
668 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
669 TALLOC_FREE(sampass);
670 return False;
673 if (lanman_pw == NULL) {
674 if (acct_ctrl & ACB_PWNOTREQ) {
675 /* this saves the pointer for the caller */
676 *hnd = sampass;
677 return True;
678 } else {
679 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
680 TALLOC_FREE(sampass);
681 return False;
685 /* Get the new lanman hash. */
686 D_P16(lanman_pw, pass2, unenc_new_pw);
688 /* Use this to get the old lanman hash. */
689 D_P16(unenc_new_pw, pass1, unenc_old_pw);
691 /* Check that the two old passwords match. */
692 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
693 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
694 TALLOC_FREE(sampass);
695 return False;
698 /* this saves the pointer for the caller */
699 *hnd = sampass;
700 return True;
703 /***********************************************************
704 Code to change the lanman hashed password.
705 It nulls out the NT hashed password as it will
706 no longer be valid.
707 NOTE this function is designed to be called as root. Check the old password
708 is correct before calling. JRA.
709 ************************************************************/
711 bool change_lanman_password(struct samu *sampass, uchar *pass2)
713 uchar null_pw[16];
714 uchar unenc_new_pw[16];
715 bool ret;
716 uint32 acct_ctrl;
717 const uint8 *pwd;
719 if (sampass == NULL) {
720 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
721 return False;
724 acct_ctrl = pdb_get_acct_ctrl(sampass);
725 pwd = pdb_get_lanman_passwd(sampass);
727 if (acct_ctrl & ACB_DISABLED) {
728 DEBUG(0,("change_lanman_password: account %s disabled.\n",
729 pdb_get_username(sampass)));
730 return False;
733 if (pwd == NULL) {
734 if (acct_ctrl & ACB_PWNOTREQ) {
735 uchar no_pw[14];
737 ZERO_STRUCT(no_pw);
739 E_P16(no_pw, null_pw);
741 pwd = null_pw;
742 } else {
743 DEBUG(0,("change_lanman_password: no lanman password !\n"));
744 return False;
748 /* Get the new lanman hash. */
749 D_P16(pwd, pass2, unenc_new_pw);
751 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
752 return False;
755 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
756 return False; /* We lose the NT hash. Sorry. */
759 if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) {
760 TALLOC_FREE(sampass);
761 /* Not quite sure what this one qualifies as, but this will do */
762 return False;
765 /* Now flush the sam_passwd struct to persistent storage */
766 ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
768 return ret;
771 /***********************************************************
772 Code to check and change the OEM hashed password.
773 ************************************************************/
775 NTSTATUS pass_oem_change(char *user,
776 uchar password_encrypted_with_lm_hash[516],
777 const uchar old_lm_hash_encrypted[16],
778 uchar password_encrypted_with_nt_hash[516],
779 const uchar old_nt_hash_encrypted[16],
780 uint32 *reject_reason)
782 char *new_passwd = NULL;
783 struct samu *sampass = NULL;
784 NTSTATUS nt_status = check_oem_password(user,
785 password_encrypted_with_lm_hash,
786 old_lm_hash_encrypted,
787 password_encrypted_with_nt_hash,
788 old_nt_hash_encrypted,
789 &sampass,
790 &new_passwd);
792 if (!NT_STATUS_IS_OK(nt_status)) {
793 return nt_status;
796 /* We've already checked the old password here.... */
797 become_root();
798 nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
799 unbecome_root();
801 memset(new_passwd, 0, strlen(new_passwd));
803 TALLOC_FREE(sampass);
805 return nt_status;
808 /***********************************************************
809 Decrypt and verify a user password change.
811 The 516 byte long buffers are encrypted with the old NT and
812 old LM passwords, and if the NT passwords are present, both
813 buffers contain a unicode string.
815 After decrypting the buffers, check the password is correct by
816 matching the old hashed passwords with the passwords in the passdb.
818 ************************************************************/
820 static NTSTATUS check_oem_password(const char *user,
821 uchar password_encrypted_with_lm_hash[516],
822 const uchar old_lm_hash_encrypted[16],
823 uchar password_encrypted_with_nt_hash[516],
824 const uchar old_nt_hash_encrypted[16],
825 struct samu **hnd,
826 char **pp_new_passwd)
828 uchar null_pw[16];
829 uchar null_ntpw[16];
830 struct samu *sampass = NULL;
831 uint8 *password_encrypted;
832 const uint8 *encryption_key;
833 const uint8 *lanman_pw, *nt_pw;
834 uint32 acct_ctrl;
835 uint32 new_pw_len;
836 uchar new_nt_hash[16];
837 uchar new_lm_hash[16];
838 uchar verifier[16];
839 char no_pw[2];
840 bool ret;
842 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
843 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
845 *hnd = NULL;
847 if ( !(sampass = samu_new( NULL )) ) {
848 return NT_STATUS_NO_MEMORY;
851 become_root();
852 ret = pdb_getsampwnam(sampass, user);
853 unbecome_root();
855 if (ret == False) {
856 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
857 TALLOC_FREE(sampass);
858 return NT_STATUS_NO_SUCH_USER;
861 acct_ctrl = pdb_get_acct_ctrl(sampass);
863 if (acct_ctrl & ACB_DISABLED) {
864 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
865 TALLOC_FREE(sampass);
866 return NT_STATUS_ACCOUNT_DISABLED;
869 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
870 /* construct a null password (in case one is needed */
871 no_pw[0] = 0;
872 no_pw[1] = 0;
873 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
874 lanman_pw = null_pw;
875 nt_pw = null_pw;
877 } else {
878 /* save pointers to passwords so we don't have to keep looking them up */
879 if (lp_lanman_auth()) {
880 lanman_pw = pdb_get_lanman_passwd(sampass);
881 } else {
882 lanman_pw = NULL;
884 nt_pw = pdb_get_nt_passwd(sampass);
887 if (nt_pw && nt_pass_set) {
888 /* IDEAL Case: passwords are in unicode, and we can
889 * read use the password encrypted with the NT hash
891 password_encrypted = password_encrypted_with_nt_hash;
892 encryption_key = nt_pw;
893 } else if (lanman_pw && lm_pass_set) {
894 /* password may still be in unicode, but use LM hash version */
895 password_encrypted = password_encrypted_with_lm_hash;
896 encryption_key = lanman_pw;
897 } else if (nt_pass_set) {
898 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
899 user));
900 TALLOC_FREE(sampass);
901 return NT_STATUS_WRONG_PASSWORD;
902 } else if (lm_pass_set) {
903 if (lp_lanman_auth()) {
904 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
905 user));
906 } else {
907 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
908 user));
910 TALLOC_FREE(sampass);
911 return NT_STATUS_WRONG_PASSWORD;
912 } else {
913 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
914 user));
915 TALLOC_FREE(sampass);
916 return NT_STATUS_WRONG_PASSWORD;
920 * Decrypt the password with the key
922 SamOEMhash( password_encrypted, encryption_key, 516);
924 if (!decode_pw_buffer(talloc_tos(),
925 password_encrypted,
926 pp_new_passwd,
927 &new_pw_len,
928 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
929 TALLOC_FREE(sampass);
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 lm password doesn't match.\n"));
952 TALLOC_FREE(sampass);
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 *hnd = sampass;
970 return NT_STATUS_OK;
973 if (lanman_pw) {
975 * check the lm verifier
977 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
978 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
979 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
980 TALLOC_FREE(sampass);
981 return NT_STATUS_WRONG_PASSWORD;
983 #ifdef DEBUG_PASSWORD
984 DEBUG(100,
985 ("check_oem_password: password %s ok\n", *pp_new_passwd));
986 #endif
987 *hnd = sampass;
988 return NT_STATUS_OK;
992 if (lanman_pw && lm_pass_set) {
994 E_deshash(*pp_new_passwd, new_lm_hash);
997 * check the lm verifier
999 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
1000 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
1001 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
1002 TALLOC_FREE(sampass);
1003 return NT_STATUS_WRONG_PASSWORD;
1006 #ifdef DEBUG_PASSWORD
1007 DEBUG(100,
1008 ("check_oem_password: password %s ok\n", *pp_new_passwd));
1009 #endif
1010 *hnd = sampass;
1011 return NT_STATUS_OK;
1014 /* should not be reached */
1015 TALLOC_FREE(sampass);
1016 return NT_STATUS_WRONG_PASSWORD;
1019 /***********************************************************
1020 This routine takes the given password and checks it against
1021 the password history. Returns True if this password has been
1022 found in the history list.
1023 ************************************************************/
1025 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
1027 uchar new_nt_p16[NT_HASH_LEN];
1028 uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
1029 const uint8 *nt_pw;
1030 const uint8 *pwhistory;
1031 bool found = False;
1032 int i;
1033 uint32 pwHisLen, curr_pwHisLen;
1035 pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
1036 if (pwHisLen == 0) {
1037 return False;
1040 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
1041 if (!pwhistory || curr_pwHisLen == 0) {
1042 return False;
1045 /* Only examine the minimum of the current history len and
1046 the stored history len. Avoids race conditions. */
1047 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
1049 nt_pw = pdb_get_nt_passwd(sampass);
1051 E_md4hash(plaintext, new_nt_p16);
1053 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
1054 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
1055 pdb_get_username(sampass) ));
1056 return True;
1059 dump_data(100, new_nt_p16, NT_HASH_LEN);
1060 dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
1062 memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
1063 for (i=0; i<pwHisLen; i++) {
1064 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
1065 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
1066 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
1067 PW_HISTORY_SALT_LEN];
1068 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1069 /* Ignore zero valued entries. */
1070 continue;
1072 /* Create salted versions of new to compare. */
1073 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1075 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1076 DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1077 pdb_get_username(sampass) ));
1078 found = True;
1079 break;
1082 return found;
1085 /***********************************************************
1086 Code to change the oem password. Changes both the lanman
1087 and NT hashes. Old_passwd is almost always NULL.
1088 NOTE this function is designed to be called as root. Check the old password
1089 is correct before calling. JRA.
1090 ************************************************************/
1092 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
1094 uint32 min_len;
1095 uint32 refuse;
1096 struct passwd *pass = NULL;
1097 const char *username = pdb_get_username(hnd);
1098 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1100 if (samr_reject_reason) {
1101 *samr_reject_reason = Undefined;
1104 /* check to see if the secdesc has previously been set to disallow */
1105 if (!pdb_get_pass_can_change(hnd)) {
1106 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1107 if (samr_reject_reason) {
1108 *samr_reject_reason = SAMR_REJECT_OTHER;
1110 return NT_STATUS_ACCOUNT_RESTRICTION;
1113 /* check to see if it is a Machine account and if the policy
1114 * denies machines to change the password. *
1115 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1116 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1117 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1118 DEBUG(1, ("Machine %s cannot change password now, "
1119 "denied by Refuse Machine Password Change policy\n",
1120 username));
1121 if (samr_reject_reason) {
1122 *samr_reject_reason = SAMR_REJECT_OTHER;
1124 return NT_STATUS_ACCOUNT_RESTRICTION;
1128 /* removed calculation here, becuase passdb now calculates
1129 based on policy. jmcd */
1130 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1131 DEBUG(1, ("user %s cannot change password now, must "
1132 "wait until %s\n", username,
1133 http_timestring(talloc_tos(), can_change_time)));
1134 if (samr_reject_reason) {
1135 *samr_reject_reason = SAMR_REJECT_OTHER;
1137 return NT_STATUS_ACCOUNT_RESTRICTION;
1140 if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1141 DEBUG(1, ("user %s cannot change password - password too short\n",
1142 username));
1143 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1144 if (samr_reject_reason) {
1145 *samr_reject_reason = SAMR_REJECT_TOO_SHORT;
1147 return NT_STATUS_PASSWORD_RESTRICTION;
1148 /* return NT_STATUS_PWD_TOO_SHORT; */
1151 if (check_passwd_history(hnd,new_passwd)) {
1152 if (samr_reject_reason) {
1153 *samr_reject_reason = SAMR_REJECT_IN_HISTORY;
1155 return NT_STATUS_PASSWORD_RESTRICTION;
1158 pass = Get_Pwnam_alloc(talloc_tos(), username);
1159 if (!pass) {
1160 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1161 return NT_STATUS_ACCESS_DENIED;
1164 /* Use external script to check password complexity */
1165 if (lp_check_password_script() && *(lp_check_password_script())) {
1166 int check_ret;
1168 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1169 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1171 if (check_ret != 0) {
1172 DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1173 if (samr_reject_reason) {
1174 *samr_reject_reason = SAMR_REJECT_COMPLEXITY;
1176 TALLOC_FREE(pass);
1177 return NT_STATUS_PASSWORD_RESTRICTION;
1182 * If unix password sync was requested, attempt to change
1183 * the /etc/passwd database first. Return failure if this cannot
1184 * be done.
1186 * This occurs before the oem change, because we don't want to
1187 * update it if chgpasswd failed.
1189 * Conditional on lp_unix_password_sync() because we don't want
1190 * to touch the unix db unless we have admin permission.
1193 if(lp_unix_password_sync() &&
1194 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1195 TALLOC_FREE(pass);
1196 return NT_STATUS_ACCESS_DENIED;
1199 TALLOC_FREE(pass);
1201 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1202 return NT_STATUS_ACCESS_DENIED;
1205 /* Now write it into the file. */
1206 return pdb_update_sam_account (hnd);