Make sure we don't clobber the stack when response consists of the empty
[Samba/gebeck_regimport.git] / source3 / smbd / chgpasswd.c
blob4192cc3a2397a8bfa8fdf18385b7c9ecde756bac
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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* These comments regard the code to change the user's unix password: */
24 /* fork a child process to exec passwd and write to its
25 * tty to change a users password. This is running as the
26 * user who is attempting to change the password.
29 /*
30 * This code was copied/borrowed and stolen from various sources.
31 * The primary source was the poppasswd.c from the authors of POPMail. This software
32 * was included as a client to change passwords using the 'passwd' program
33 * on the remote machine.
35 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
36 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
37 * and rights to modify, distribute or incorporate this change to the CAP suite or
38 * using it for any other reason are granted, so long as this disclaimer is left intact.
42 This code was hacked considerably for inclusion in Samba, primarily
43 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
44 of the "password chat" option, which allows the easy runtime
45 specification of the expected sequence of events to change a
46 password.
49 #include "includes.h"
51 #ifdef HAVE_WORKING_CRACKLIB
52 #include <crack.h>
54 #ifndef HAVE_CRACKLIB_DICTPATH
55 #ifndef CRACKLIB_DICTPATH
56 #define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
57 #endif
58 #endif
59 #endif
61 extern struct passdb_ops pdb_ops;
63 static NTSTATUS check_oem_password(const char *user,
64 uchar password_encrypted_with_lm_hash[516],
65 const uchar old_lm_hash_encrypted[16],
66 uchar password_encrypted_with_nt_hash[516],
67 const uchar old_nt_hash_encrypted[16],
68 SAM_ACCOUNT **hnd, char *new_passwd,
69 int new_passwd_size);
71 #if ALLOW_CHANGE_PASSWORD
73 static int findpty(char **slave)
75 int master;
76 static fstring line;
77 DIR *dirp;
78 const char *dpname;
80 #if defined(HAVE_GRANTPT)
81 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
82 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
84 grantpt(master);
85 unlockpt(master);
86 *slave = (char *)ptsname(master);
87 if (*slave == NULL)
89 DEBUG(0,
90 ("findpty: Unable to create master/slave pty pair.\n"));
91 /* Stop fd leak on error. */
92 close(master);
93 return -1;
95 else
97 DEBUG(10,
98 ("findpty: Allocated slave pty %s\n", *slave));
99 return (master);
102 #endif /* HAVE_GRANTPT */
104 fstrcpy(line, "/dev/ptyXX");
106 dirp = opendir("/dev");
107 if (!dirp)
108 return (-1);
109 while ((dpname = readdirname(dirp)) != NULL)
111 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
113 DEBUG(3,
114 ("pty: try to open %s, line was %s\n", dpname,
115 line));
116 line[8] = dpname[3];
117 line[9] = dpname[4];
118 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
120 DEBUG(3, ("pty: opened %s\n", line));
121 line[5] = 't';
122 *slave = line;
123 closedir(dirp);
124 return (master);
128 closedir(dirp);
129 return (-1);
132 static int dochild(int master, const char *slavedev, const struct passwd *pass,
133 const char *passwordprogram, BOOL as_root)
135 int slave;
136 struct termios stermios;
137 gid_t gid;
138 uid_t uid;
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 = sys_open(slavedev, O_RDWR, 0)) < 0)
163 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
164 return (False);
166 #ifdef I_PUSH
167 ioctl(slave, I_PUSH, "ptem");
168 ioctl(slave, I_PUSH, "ldterm");
169 #elif defined(TIOCSCTTY)
170 if (ioctl(slave, TIOCSCTTY, 0) < 0)
172 DEBUG(3, ("Error in ioctl call for slave pty\n"));
173 /* return(False); */
175 #endif
177 /* Close master. */
178 close(master);
180 /* Make slave stdin/out/err of child. */
182 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
184 DEBUG(3, ("Could not re-direct stdin\n"));
185 return (False);
187 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
189 DEBUG(3, ("Could not re-direct stdout\n"));
190 return (False);
192 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
194 DEBUG(3, ("Could not re-direct stderr\n"));
195 return (False);
197 if (slave > 2)
198 close(slave);
200 /* Set proper terminal attributes - no echo, canonical input processing,
201 no map NL to CR/NL on output. */
203 if (tcgetattr(0, &stermios) < 0)
205 DEBUG(3,
206 ("could not read default terminal attributes on pty\n"));
207 return (False);
209 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
210 stermios.c_lflag |= ICANON;
211 #ifdef ONLCR
212 stermios.c_oflag &= ~(ONLCR);
213 #endif
214 if (tcsetattr(0, TCSANOW, &stermios) < 0)
216 DEBUG(3, ("could not set attributes of pty\n"));
217 return (False);
220 /* make us completely into the right uid */
221 if (!as_root)
223 become_user_permanently(uid, gid);
226 DEBUG(10,
227 ("Invoking '%s' as password change program.\n",
228 passwordprogram));
230 /* execl() password-change application */
231 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
233 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
234 return (False);
236 return (True);
239 static int expect(int master, char *issue, char *expected)
241 pstring buffer;
242 int attempts, timeout, nread, len;
243 BOOL match = False;
245 for (attempts = 0; attempts < 2; attempts++) {
246 if (!strequal(issue, ".")) {
247 if (lp_passwd_chat_debug())
248 DEBUG(100, ("expect: sending [%s]\n", issue));
250 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
251 DEBUG(2,("expect: (short) write returned %d\n", len ));
252 return False;
256 if (strequal(expected, "."))
257 return True;
259 /* Initial timeout. */
260 timeout = lp_passwd_chat_timeout() * 1000;
261 nread = 0;
262 buffer[nread] = 0;
264 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
265 sizeof(buffer) - nread - 1,
266 timeout)) > 0) {
267 nread += len;
268 buffer[nread] = 0;
271 /* Eat leading/trailing whitespace before match. */
272 pstring str;
273 pstrcpy( str, buffer);
274 trim_char( str, ' ', ' ');
276 if ((match = (unix_wild_match(expected, str) == 0))) {
277 /* Now data has started to return, lower timeout. */
278 timeout = lp_passwd_chat_timeout() * 100;
283 if (lp_passwd_chat_debug())
284 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
285 expected, buffer, match ? "yes" : "no" ));
287 if (match)
288 break;
290 if (len < 0) {
291 DEBUG(2, ("expect: %s\n", strerror(errno)));
292 return False;
296 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
297 return match;
300 static void pwd_sub(char *buf)
302 all_string_sub(buf, "\\n", "\n", 0);
303 all_string_sub(buf, "\\r", "\r", 0);
304 all_string_sub(buf, "\\s", " ", 0);
305 all_string_sub(buf, "\\t", "\t", 0);
308 static int talktochild(int master, const char *seq)
310 int count = 0;
311 fstring issue, expected;
313 fstrcpy(issue, ".");
315 while (next_token(&seq, expected, NULL, sizeof(expected)))
317 pwd_sub(expected);
318 count++;
320 if (!expect(master, issue, expected))
322 DEBUG(3, ("Response %d incorrect\n", count));
323 return False;
326 if (!next_token(&seq, issue, NULL, sizeof(issue)))
327 fstrcpy(issue, ".");
329 pwd_sub(issue);
331 if (!strequal(issue, ".")) {
332 /* we have one final issue to send */
333 fstrcpy(expected, ".");
334 if (!expect(master, issue, expected))
335 return False;
338 return (count > 0);
341 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
342 char *chatsequence, BOOL as_root)
344 char *slavedev;
345 int master;
346 pid_t pid, wpid;
347 int wstat;
348 BOOL chstat = False;
350 if (pass == NULL) {
351 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
352 return False;
355 /* allocate a pseudo-terminal device */
356 if ((master = findpty(&slavedev)) < 0) {
357 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
358 return (False);
362 * We need to temporarily stop CatchChild from eating
363 * SIGCLD signals as it also eats the exit status code. JRA.
366 CatchChildLeaveStatus();
368 if ((pid = sys_fork()) < 0) {
369 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
370 close(master);
371 CatchChild();
372 return (False);
375 /* we now have a pty */
376 if (pid > 0) { /* This is the parent process */
377 if ((chstat = talktochild(master, chatsequence)) == False) {
378 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
379 kill(pid, SIGKILL); /* be sure to end this process */
382 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
383 if (errno == EINTR) {
384 errno = 0;
385 continue;
387 break;
390 if (wpid < 0) {
391 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
392 close(master);
393 CatchChild();
394 return (False);
398 * Go back to ignoring children.
400 CatchChild();
402 close(master);
404 if (pid != wpid) {
405 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
406 return (False);
408 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
409 DEBUG(3, ("chat_with_program: The process exited with status %d \
410 while we were waiting\n", WEXITSTATUS(wstat)));
411 return (False);
413 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
414 else if (WIFSIGNALLED(wstat)) {
415 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
416 while we were waiting\n", WTERMSIG(wstat)));
417 return (False);
419 #endif
420 } else {
421 /* CHILD */
424 * Lose any oplock capabilities.
426 oplock_set_capability(False, False);
428 /* make sure it doesn't freeze */
429 alarm(20);
431 if (as_root)
432 become_root();
434 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
435 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
436 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
438 if (as_root)
439 unbecome_root();
442 * The child should never return from dochild() ....
445 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
446 exit(1);
449 if (chstat)
450 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
451 (chstat ? "" : "un"), pass->pw_name));
452 return (chstat);
455 BOOL chgpasswd(const char *name, const struct passwd *pass,
456 const char *oldpass, const char *newpass, BOOL as_root)
458 pstring passwordprogram;
459 pstring chatsequence;
460 size_t i;
461 size_t len;
463 if (!oldpass) {
464 oldpass = "";
467 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
469 #if DEBUG_PASSWORD
470 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
471 #endif
473 /* Take the passed information and test it for minimum criteria */
475 /* Password is same as old password */
476 if (strcmp(oldpass, newpass) == 0) {
477 /* don't allow same password */
478 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
479 return (False); /* inform the user */
483 * Check the old and new passwords don't contain any control
484 * characters.
487 len = strlen(oldpass);
488 for (i = 0; i < len; i++) {
489 if (iscntrl((int)oldpass[i])) {
490 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
491 return False;
495 len = strlen(newpass);
496 for (i = 0; i < len; i++) {
497 if (iscntrl((int)newpass[i])) {
498 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
499 return False;
503 #ifdef WITH_PAM
504 if (lp_pam_password_change()) {
505 BOOL ret;
507 if (as_root)
508 become_root();
510 if (pass) {
511 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
512 } else {
513 ret = smb_pam_passchange(name, oldpass, newpass);
516 if (as_root)
517 unbecome_root();
519 return ret;
521 #endif
523 /* A non-PAM password change just doen't make sense without a valid local user */
525 if (pass == NULL) {
526 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
527 return False;
530 pstrcpy(passwordprogram, lp_passwd_program());
531 pstrcpy(chatsequence, lp_passwd_chat());
533 if (!*chatsequence) {
534 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
535 return (False);
538 if (!*passwordprogram) {
539 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
540 return (False);
543 if (as_root) {
544 /* The password program *must* contain the user name to work. Fail if not. */
545 if (strstr_m(passwordprogram, "%u") == NULL) {
546 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
547 the string %%u, and the given string %s does not.\n", passwordprogram ));
548 return False;
552 pstring_sub(passwordprogram, "%u", name);
553 /* note that we do NOT substitute the %o and %n in the password program
554 as this would open up a security hole where the user could use
555 a new password containing shell escape characters */
557 pstring_sub(chatsequence, "%u", name);
558 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
559 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
560 return (chat_with_program
561 (passwordprogram, pass, chatsequence, as_root));
564 #else /* ALLOW_CHANGE_PASSWORD */
566 BOOL chgpasswd(const char *name, const struct passwd *pass,
567 const char *oldpass, const char *newpass, BOOL as_root)
569 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
570 return (False);
572 #endif /* ALLOW_CHANGE_PASSWORD */
574 /***********************************************************
575 Code to check the lanman hashed password.
576 ************************************************************/
578 BOOL check_lanman_password(char *user, uchar * pass1,
579 uchar * pass2, SAM_ACCOUNT **hnd)
581 uchar unenc_new_pw[16];
582 uchar unenc_old_pw[16];
583 SAM_ACCOUNT *sampass = NULL;
584 uint16 acct_ctrl;
585 const uint8 *lanman_pw;
586 BOOL ret;
588 become_root();
589 ret = pdb_getsampwnam(sampass, user);
590 unbecome_root();
592 if (ret == False) {
593 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
594 pdb_free_sam(&sampass);
595 return False;
598 acct_ctrl = pdb_get_acct_ctrl (sampass);
599 lanman_pw = pdb_get_lanman_passwd (sampass);
601 if (acct_ctrl & ACB_DISABLED) {
602 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
603 pdb_free_sam(&sampass);
604 return False;
607 if (lanman_pw == NULL) {
608 if (acct_ctrl & ACB_PWNOTREQ) {
609 /* this saves the pointer for the caller */
610 *hnd = sampass;
611 return True;
612 } else {
613 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
614 pdb_free_sam(&sampass);
615 return False;
619 /* Get the new lanman hash. */
620 D_P16(lanman_pw, pass2, unenc_new_pw);
622 /* Use this to get the old lanman hash. */
623 D_P16(unenc_new_pw, pass1, unenc_old_pw);
625 /* Check that the two old passwords match. */
626 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
627 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
628 pdb_free_sam(&sampass);
629 return False;
632 /* this saves the pointer for the caller */
633 *hnd = sampass;
634 return True;
637 /***********************************************************
638 Code to change the lanman hashed password.
639 It nulls out the NT hashed password as it will
640 no longer be valid.
641 NOTE this function is designed to be called as root. Check the old password
642 is correct before calling. JRA.
643 ************************************************************/
645 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
647 static uchar null_pw[16];
648 uchar unenc_new_pw[16];
649 BOOL ret;
650 uint16 acct_ctrl;
651 const uint8 *pwd;
653 if (sampass == NULL) {
654 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
655 return False;
658 acct_ctrl = pdb_get_acct_ctrl(sampass);
659 pwd = pdb_get_lanman_passwd(sampass);
661 if (acct_ctrl & ACB_DISABLED) {
662 DEBUG(0,("change_lanman_password: account %s disabled.\n",
663 pdb_get_username(sampass)));
664 return False;
667 if (pwd == NULL) {
668 if (acct_ctrl & ACB_PWNOTREQ) {
669 uchar no_pw[14];
670 memset(no_pw, '\0', 14);
671 E_P16(no_pw, null_pw);
673 /* Get the new lanman hash. */
674 D_P16(null_pw, pass2, unenc_new_pw);
675 } else {
676 DEBUG(0,("change_lanman_password: no lanman password !\n"));
677 return False;
679 } else {
680 /* Get the new lanman hash. */
681 D_P16(pwd, pass2, unenc_new_pw);
684 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
685 return False;
688 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
689 return False; /* We lose the NT hash. Sorry. */
692 if (!pdb_set_pass_changed_now (sampass)) {
693 pdb_free_sam(&sampass);
694 /* Not quite sure what this one qualifies as, but this will do */
695 return False;
698 /* Now flush the sam_passwd struct to persistent storage */
699 ret = pdb_update_sam_account (sampass);
701 return ret;
704 /***********************************************************
705 Code to check and change the OEM hashed password.
706 ************************************************************/
708 NTSTATUS pass_oem_change(char *user,
709 uchar password_encrypted_with_lm_hash[516],
710 const uchar old_lm_hash_encrypted[16],
711 uchar password_encrypted_with_nt_hash[516],
712 const uchar old_nt_hash_encrypted[16])
714 pstring new_passwd;
715 SAM_ACCOUNT *sampass = NULL;
716 NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
717 old_lm_hash_encrypted,
718 password_encrypted_with_nt_hash,
719 old_nt_hash_encrypted,
720 &sampass, new_passwd, sizeof(new_passwd));
722 if (!NT_STATUS_IS_OK(nt_status))
723 return nt_status;
725 /* We've already checked the old password here.... */
726 become_root();
727 nt_status = change_oem_password(sampass, NULL, new_passwd, True);
728 unbecome_root();
730 memset(new_passwd, 0, sizeof(new_passwd));
732 pdb_free_sam(&sampass);
734 return nt_status;
737 /***********************************************************
738 Decrypt and verify a user password change.
740 The 516 byte long buffers are encrypted with the old NT and
741 old LM passwords, and if the NT passwords are present, both
742 buffers contain a unicode string.
744 After decrypting the buffers, check the password is correct by
745 matching the old hashed passwords with the passwords in the passdb.
747 ************************************************************/
749 static NTSTATUS check_oem_password(const char *user,
750 uchar password_encrypted_with_lm_hash[516],
751 const uchar old_lm_hash_encrypted[16],
752 uchar password_encrypted_with_nt_hash[516],
753 const uchar old_nt_hash_encrypted[16],
754 SAM_ACCOUNT **hnd, char *new_passwd,
755 int new_passwd_size)
757 static uchar null_pw[16];
758 static uchar null_ntpw[16];
759 SAM_ACCOUNT *sampass = NULL;
760 char *password_encrypted;
761 const char *encryption_key;
762 const uint8 *lanman_pw, *nt_pw;
763 uint16 acct_ctrl;
764 uint32 new_pw_len;
765 uchar new_nt_hash[16];
766 uchar old_nt_hash_plain[16];
767 uchar new_lm_hash[16];
768 uchar old_lm_hash_plain[16];
769 char no_pw[2];
770 BOOL ret;
772 BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
773 BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
775 *hnd = NULL;
777 pdb_init_sam(&sampass);
779 become_root();
780 ret = pdb_getsampwnam(sampass, user);
781 unbecome_root();
783 if (ret == False) {
784 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
785 pdb_free_sam(&sampass);
786 return NT_STATUS_NO_SUCH_USER;
789 acct_ctrl = pdb_get_acct_ctrl(sampass);
791 if (acct_ctrl & ACB_DISABLED) {
792 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
793 pdb_free_sam(&sampass);
794 return NT_STATUS_ACCOUNT_DISABLED;
797 if (acct_ctrl & ACB_PWNOTREQ && lp_null_passwords()) {
798 /* construct a null password (in case one is needed */
799 no_pw[0] = 0;
800 no_pw[1] = 0;
801 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
802 lanman_pw = null_pw;
803 nt_pw = null_pw;
805 } else {
806 /* save pointers to passwords so we don't have to keep looking them up */
807 if (lp_lanman_auth()) {
808 lanman_pw = pdb_get_lanman_passwd(sampass);
809 } else {
810 lanman_pw = NULL;
812 nt_pw = pdb_get_nt_passwd(sampass);
815 if (nt_pw && nt_pass_set) {
816 /* IDEAL Case: passwords are in unicode, and we can
817 * read use the password encrypted with the NT hash
819 password_encrypted = password_encrypted_with_nt_hash;
820 encryption_key = nt_pw;
821 } else if (lanman_pw && lm_pass_set) {
822 /* password may still be in unicode, but use LM hash version */
823 password_encrypted = password_encrypted_with_lm_hash;
824 encryption_key = lanman_pw;
825 } else if (nt_pass_set) {
826 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
827 user));
828 pdb_free_sam(&sampass);
829 return NT_STATUS_WRONG_PASSWORD;
830 } else if (lm_pass_set) {
831 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
832 user));
833 pdb_free_sam(&sampass);
834 return NT_STATUS_WRONG_PASSWORD;
835 } else {
836 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
837 user));
838 pdb_free_sam(&sampass);
839 return NT_STATUS_WRONG_PASSWORD;
843 * Decrypt the password with the key
845 SamOEMhash( password_encrypted, encryption_key, 516);
847 if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
848 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
849 pdb_free_sam(&sampass);
850 return NT_STATUS_WRONG_PASSWORD;
854 * To ensure we got the correct new password, hash it and
855 * use it as a key to test the passed old password.
858 if (nt_pass_set) {
859 /* NT passwords, verify the NT hash. */
861 /* Calculate the MD4 hash (NT compatible) of the password */
862 memset(new_nt_hash, '\0', 16);
863 E_md4hash(new_passwd, new_nt_hash);
865 if (nt_pw) {
867 * Now use new_nt_hash as the key to see if the old
868 * password matches.
870 D_P16(new_nt_hash, old_nt_hash_encrypted, old_nt_hash_plain);
872 if (memcmp(nt_pw, old_nt_hash_plain, 16)) {
873 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
874 pdb_free_sam(&sampass);
875 return NT_STATUS_WRONG_PASSWORD;
878 /* We could check the LM password here, but there is
879 * little point, we already know the password is
880 * correct, and the LM password might not even be
881 * present. */
883 /* Further, LM hash generation algorithms
884 * differ with charset, so we could
885 * incorrectly fail a perfectly valid password
886 * change */
887 #ifdef DEBUG_PASSWORD
888 DEBUG(100,
889 ("check_oem_password: password %s ok\n", new_passwd));
890 #endif
891 *hnd = sampass;
892 return NT_STATUS_OK;
895 if (lanman_pw) {
897 * Now use new_nt_hash as the key to see if the old
898 * LM password matches.
900 D_P16(new_nt_hash, old_lm_hash_encrypted, old_lm_hash_plain);
902 if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
903 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
904 pdb_free_sam(&sampass);
905 return NT_STATUS_WRONG_PASSWORD;
907 #ifdef DEBUG_PASSWORD
908 DEBUG(100,
909 ("check_oem_password: password %s ok\n", new_passwd));
910 #endif
911 *hnd = sampass;
912 return NT_STATUS_OK;
916 if (lanman_pw && lm_pass_set) {
918 E_deshash(new_passwd, new_lm_hash);
921 * Now use new_lm_hash as the key to see if the old
922 * password matches.
924 D_P16(new_lm_hash, old_lm_hash_encrypted, old_lm_hash_plain);
926 if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
927 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
928 pdb_free_sam(&sampass);
929 return NT_STATUS_WRONG_PASSWORD;
932 #ifdef DEBUG_PASSWORD
933 DEBUG(100,
934 ("check_oem_password: password %s ok\n", new_passwd));
935 #endif
936 *hnd = sampass;
937 return NT_STATUS_OK;
940 /* should not be reached */
941 pdb_free_sam(&sampass);
942 return NT_STATUS_WRONG_PASSWORD;
945 /***********************************************************
946 Code to change the oem password. Changes both the lanman
947 and NT hashes. Old_passwd is almost always NULL.
948 NOTE this function is designed to be called as root. Check the old password
949 is correct before calling. JRA.
950 ************************************************************/
952 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
954 struct passwd *pass;
956 BOOL ret;
957 uint32 min_len;
959 if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
960 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
961 pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
962 return NT_STATUS_PASSWORD_RESTRICTION;
965 if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
966 DEBUG(1, ("user %s cannot change password - password too short\n",
967 pdb_get_username(hnd)));
968 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
969 return NT_STATUS_PASSWORD_RESTRICTION;
970 /* return NT_STATUS_PWD_TOO_SHORT; */
973 /* Take the passed information and test it for minimum criteria */
974 /* Minimum password length */
975 if (strlen(new_passwd) < lp_min_passwd_length()) {
976 /* too short, must be at least MINPASSWDLENGTH */
977 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
978 pdb_get_username(hnd), lp_min_passwd_length()));
979 return NT_STATUS_PASSWORD_RESTRICTION;
980 /* return NT_STATUS_PWD_TOO_SHORT; */
983 pass = Get_Pwnam(pdb_get_username(hnd));
984 if (!pass) {
985 DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
988 #ifdef HAVE_WORKING_CRACKLIB
989 if (pass) {
990 /* if we can, become the user to overcome internal cracklib sillyness */
991 if (!push_sec_ctx())
992 return NT_STATUS_UNSUCCESSFUL;
994 set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
995 set_re_uid();
998 if (lp_use_cracklib()) {
999 const char *crack_check_reason;
1000 DEBUG(4, ("change_oem_password: Checking password for user [%s]"
1001 " against cracklib. \n", pdb_get_username(hnd)));
1002 DEBUGADD(4, ("If this is your last message, then something is "
1003 "wrong with cracklib, it might be missing it's "
1004 "dictionaries at %s\n",
1005 CRACKLIB_DICTPATH));
1006 dbgflush();
1008 crack_check_reason = FascistCheck(new_passwd, (char *)CRACKLIB_DICTPATH);
1009 if (crack_check_reason) {
1010 DEBUG(1, ("Password Change: user [%s], "
1011 "New password failed cracklib test - %s\n",
1012 pdb_get_username(hnd), crack_check_reason));
1014 /* get back to where we should be */
1015 if (pass)
1016 pop_sec_ctx();
1017 return NT_STATUS_PASSWORD_RESTRICTION;
1021 if (pass)
1022 pop_sec_ctx();
1023 #endif
1026 * If unix password sync was requested, attempt to change
1027 * the /etc/passwd database first. Return failure if this cannot
1028 * be done.
1030 * This occurs before the oem change, because we don't want to
1031 * update it if chgpasswd failed.
1033 * Conditional on lp_unix_password_sync() because we don't want
1034 * to touch the unix db unless we have admin permission.
1037 if(lp_unix_password_sync() &&
1038 !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) {
1039 return NT_STATUS_ACCESS_DENIED;
1042 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1043 return NT_STATUS_ACCESS_DENIED;
1046 /* Now write it into the file. */
1047 ret = pdb_update_sam_account (hnd);
1049 if (!ret) {
1050 return NT_STATUS_ACCESS_DENIED;
1053 return NT_STATUS_OK;