On systems without a working cracklib, ensure we don't include the header
[Samba/gebeck_regimport.git] / source3 / smbd / chgpasswd.c
blob692e82680dbafad4978f173937a2966b73e5ea3e
1 /*
2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2002
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 /* fork a child process to exec passwd and write to its
23 * tty to change a users password. This is running as the
24 * user who is attempting to change the password.
27 /*
28 * This code was copied/borrowed and stolen from various sources.
29 * The primary source was the poppasswd.c from the authors of POPMail. This software
30 * was included as a client to change passwords using the 'passwd' program
31 * on the remote machine.
33 * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
34 * is defined in the compiler directives located in the Makefile.
36 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
37 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
38 * and rights to modify, distribute or incorporate this change to the CAP suite or
39 * using it for any other reason are granted, so long as this disclaimer is left intact.
43 This code was hacked considerably for inclusion in Samba, primarily
44 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
45 of the "password chat" option, which allows the easy runtime
46 specification of the expected sequence of events to change a
47 password.
50 #include "includes.h"
52 #ifdef HAVE_WORKING_CRACKLIB
53 #include <crack.h>
55 #ifndef HAVE_CRACKLIB_DICTPATH
56 #ifndef CRACKLIB_DICTPATH
57 #define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
58 #endif
59 #endif
60 #endif
62 extern struct passdb_ops pdb_ops;
64 static NTSTATUS check_oem_password(const char *user,
65 uchar * lmdata, const uchar * lmhash,
66 const uchar * ntdata, const uchar * nthash,
67 SAM_ACCOUNT **hnd, char *new_passwd,
68 int new_passwd_size);
70 #if ALLOW_CHANGE_PASSWORD
72 static int findpty(char **slave)
74 int master;
75 static fstring line;
76 DIR *dirp;
77 const char *dpname;
79 #if defined(HAVE_GRANTPT)
80 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
81 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
83 grantpt(master);
84 unlockpt(master);
85 *slave = (char *)ptsname(master);
86 if (*slave == NULL)
88 DEBUG(0,
89 ("findpty: Unable to create master/slave pty pair.\n"));
90 /* Stop fd leak on error. */
91 close(master);
92 return -1;
94 else
96 DEBUG(10,
97 ("findpty: Allocated slave pty %s\n", *slave));
98 return (master);
101 #endif /* HAVE_GRANTPT */
103 fstrcpy(line, "/dev/ptyXX");
105 dirp = opendir("/dev");
106 if (!dirp)
107 return (-1);
108 while ((dpname = readdirname(dirp)) != NULL)
110 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
112 DEBUG(3,
113 ("pty: try to open %s, line was %s\n", dpname,
114 line));
115 line[8] = dpname[3];
116 line[9] = dpname[4];
117 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
119 DEBUG(3, ("pty: opened %s\n", line));
120 line[5] = 't';
121 *slave = line;
122 closedir(dirp);
123 return (master);
127 closedir(dirp);
128 return (-1);
131 static int dochild(int master, const char *slavedev, const struct passwd *pass,
132 const char *passwordprogram, BOOL as_root)
134 int slave;
135 struct termios stermios;
136 gid_t gid;
137 uid_t uid;
139 if (pass == NULL)
141 DEBUG(0,
142 ("dochild: user doesn't exist in the UNIX password database.\n"));
143 return False;
146 gid = pass->pw_gid;
147 uid = pass->pw_uid;
149 gain_root_privilege();
151 /* Start new session - gets rid of controlling terminal. */
152 if (setsid() < 0)
154 DEBUG(3,
155 ("Weirdness, couldn't let go of controlling terminal\n"));
156 return (False);
159 /* Open slave pty and acquire as new controlling terminal. */
160 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
162 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
163 return (False);
165 #ifdef I_PUSH
166 ioctl(slave, I_PUSH, "ptem");
167 ioctl(slave, I_PUSH, "ldterm");
168 #elif defined(TIOCSCTTY)
169 if (ioctl(slave, TIOCSCTTY, 0) < 0)
171 DEBUG(3, ("Error in ioctl call for slave pty\n"));
172 /* return(False); */
174 #endif
176 /* Close master. */
177 close(master);
179 /* Make slave stdin/out/err of child. */
181 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
183 DEBUG(3, ("Could not re-direct stdin\n"));
184 return (False);
186 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
188 DEBUG(3, ("Could not re-direct stdout\n"));
189 return (False);
191 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
193 DEBUG(3, ("Could not re-direct stderr\n"));
194 return (False);
196 if (slave > 2)
197 close(slave);
199 /* Set proper terminal attributes - no echo, canonical input processing,
200 no map NL to CR/NL on output. */
202 if (tcgetattr(0, &stermios) < 0)
204 DEBUG(3,
205 ("could not read default terminal attributes on pty\n"));
206 return (False);
208 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
209 stermios.c_lflag |= ICANON;
210 #ifdef ONLCR
211 stermios.c_oflag &= ~(ONLCR);
212 #endif
213 if (tcsetattr(0, TCSANOW, &stermios) < 0)
215 DEBUG(3, ("could not set attributes of pty\n"));
216 return (False);
219 /* make us completely into the right uid */
220 if (!as_root)
222 become_user_permanently(uid, gid);
225 DEBUG(10,
226 ("Invoking '%s' as password change program.\n",
227 passwordprogram));
229 /* execl() password-change application */
230 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
232 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
233 return (False);
235 return (True);
238 static int expect(int master, char *issue, char *expected)
240 pstring buffer;
241 int attempts, timeout, nread, len;
242 BOOL match = False;
244 for (attempts = 0; attempts < 2; attempts++) {
245 if (!strequal(issue, ".")) {
246 if (lp_passwd_chat_debug())
247 DEBUG(100, ("expect: sending [%s]\n", issue));
249 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
250 DEBUG(2,("expect: (short) write returned %d\n", len ));
251 return False;
255 if (strequal(expected, "."))
256 return True;
258 /* Initial timeout. */
259 timeout = lp_passwd_chat_timeout() * 1000;
260 nread = 0;
261 buffer[nread] = 0;
263 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
264 sizeof(buffer) - nread - 1,
265 timeout)) > 0) {
266 nread += len;
267 buffer[nread] = 0;
270 /* Eat leading/trailing whitespace before match. */
271 pstring str;
272 pstrcpy( str, buffer);
273 trim_char( str, ' ', ' ');
275 if ((match = (unix_wild_match(expected, str) == 0))) {
276 /* Now data has started to return, lower timeout. */
277 timeout = lp_passwd_chat_timeout() * 100;
282 if (lp_passwd_chat_debug())
283 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
284 expected, buffer, match ? "yes" : "no" ));
286 if (match)
287 break;
289 if (len < 0) {
290 DEBUG(2, ("expect: %s\n", strerror(errno)));
291 return False;
295 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
296 return match;
299 static void pwd_sub(char *buf)
301 all_string_sub(buf, "\\n", "\n", 0);
302 all_string_sub(buf, "\\r", "\r", 0);
303 all_string_sub(buf, "\\s", " ", 0);
304 all_string_sub(buf, "\\t", "\t", 0);
307 static int talktochild(int master, const char *seq)
309 int count = 0;
310 fstring issue, expected;
312 fstrcpy(issue, ".");
314 while (next_token(&seq, expected, NULL, sizeof(expected)))
316 pwd_sub(expected);
317 count++;
319 if (!expect(master, issue, expected))
321 DEBUG(3, ("Response %d incorrect\n", count));
322 return False;
325 if (!next_token(&seq, issue, NULL, sizeof(issue)))
326 fstrcpy(issue, ".");
328 pwd_sub(issue);
330 if (!strequal(issue, ".")) {
331 /* we have one final issue to send */
332 fstrcpy(expected, ".");
333 if (!expect(master, issue, expected))
334 return False;
337 return (count > 0);
340 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
341 char *chatsequence, BOOL as_root)
343 char *slavedev;
344 int master;
345 pid_t pid, wpid;
346 int wstat;
347 BOOL chstat = False;
349 if (pass == NULL) {
350 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
351 return False;
354 /* allocate a pseudo-terminal device */
355 if ((master = findpty(&slavedev)) < 0) {
356 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
357 return (False);
361 * We need to temporarily stop CatchChild from eating
362 * SIGCLD signals as it also eats the exit status code. JRA.
365 CatchChildLeaveStatus();
367 if ((pid = sys_fork()) < 0) {
368 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
369 close(master);
370 CatchChild();
371 return (False);
374 /* we now have a pty */
375 if (pid > 0) { /* This is the parent process */
376 if ((chstat = talktochild(master, chatsequence)) == False) {
377 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
378 kill(pid, SIGKILL); /* be sure to end this process */
381 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
382 if (errno == EINTR) {
383 errno = 0;
384 continue;
386 break;
389 if (wpid < 0) {
390 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
391 close(master);
392 CatchChild();
393 return (False);
397 * Go back to ignoring children.
399 CatchChild();
401 close(master);
403 if (pid != wpid) {
404 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
405 return (False);
407 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
408 DEBUG(3, ("chat_with_program: The process exited with status %d \
409 while we were waiting\n", WEXITSTATUS(wstat)));
410 return (False);
412 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
413 else if (WIFSIGNALLED(wstat)) {
414 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
415 while we were waiting\n", WTERMSIG(wstat)));
416 return (False);
418 #endif
419 } else {
420 /* CHILD */
423 * Lose any oplock capabilities.
425 oplock_set_capability(False, False);
427 /* make sure it doesn't freeze */
428 alarm(20);
430 if (as_root)
431 become_root();
433 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
434 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
435 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
437 if (as_root)
438 unbecome_root();
441 * The child should never return from dochild() ....
444 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
445 exit(1);
448 if (chstat)
449 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
450 (chstat ? "" : "un"), pass->pw_name));
451 return (chstat);
454 BOOL chgpasswd(const char *name, const struct passwd *pass,
455 const char *oldpass, const char *newpass, BOOL as_root)
457 pstring passwordprogram;
458 pstring chatsequence;
459 size_t i;
460 size_t len;
462 if (!oldpass) {
463 oldpass = "";
466 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
468 #if DEBUG_PASSWORD
469 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
470 #endif
472 /* Take the passed information and test it for minimum criteria */
474 /* Password is same as old password */
475 if (strcmp(oldpass, newpass) == 0) {
476 /* don't allow same password */
477 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
478 return (False); /* inform the user */
482 * Check the old and new passwords don't contain any control
483 * characters.
486 len = strlen(oldpass);
487 for (i = 0; i < len; i++) {
488 if (iscntrl((int)oldpass[i])) {
489 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
490 return False;
494 len = strlen(newpass);
495 for (i = 0; i < len; i++) {
496 if (iscntrl((int)newpass[i])) {
497 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
498 return False;
502 #ifdef WITH_PAM
503 if (lp_pam_password_change()) {
504 BOOL ret;
506 if (as_root)
507 become_root();
509 if (pass) {
510 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
511 } else {
512 ret = smb_pam_passchange(name, oldpass, newpass);
515 if (as_root)
516 unbecome_root();
518 return ret;
520 #endif
522 /* A non-PAM password change just doen't make sense without a valid local user */
524 if (pass == NULL) {
525 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
526 return False;
529 pstrcpy(passwordprogram, lp_passwd_program());
530 pstrcpy(chatsequence, lp_passwd_chat());
532 if (!*chatsequence) {
533 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
534 return (False);
537 if (!*passwordprogram) {
538 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
539 return (False);
542 if (as_root) {
543 /* The password program *must* contain the user name to work. Fail if not. */
544 if (strstr(passwordprogram, "%u") == NULL) {
545 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
546 the string %%u, and the given string %s does not.\n", passwordprogram ));
547 return False;
551 pstring_sub(passwordprogram, "%u", name);
552 /* note that we do NOT substitute the %o and %n in the password program
553 as this would open up a security hole where the user could use
554 a new password containing shell escape characters */
556 pstring_sub(chatsequence, "%u", name);
557 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
558 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
559 return (chat_with_program
560 (passwordprogram, pass, chatsequence, as_root));
563 #else /* ALLOW_CHANGE_PASSWORD */
565 BOOL chgpasswd(const char *name, const struct passwd *pass,
566 const char *oldpass, const char *newpass, BOOL as_root)
568 DEBUG(0, ("chgpasswd: Password changing not compiled in (user=%s)\n", name));
569 return (False);
571 #endif /* ALLOW_CHANGE_PASSWORD */
573 /***********************************************************
574 Code to check the lanman hashed password.
575 ************************************************************/
577 BOOL check_lanman_password(char *user, uchar * pass1,
578 uchar * pass2, SAM_ACCOUNT **hnd)
580 uchar unenc_new_pw[16];
581 uchar unenc_old_pw[16];
582 SAM_ACCOUNT *sampass = NULL;
583 uint16 acct_ctrl;
584 const uint8 *lanman_pw;
585 BOOL ret;
587 become_root();
588 ret = pdb_getsampwnam(sampass, user);
589 unbecome_root();
591 if (ret == False) {
592 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
593 pdb_free_sam(&sampass);
594 return False;
597 acct_ctrl = pdb_get_acct_ctrl (sampass);
598 lanman_pw = pdb_get_lanman_passwd (sampass);
600 if (acct_ctrl & ACB_DISABLED) {
601 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
602 pdb_free_sam(&sampass);
603 return False;
606 if (lanman_pw == NULL) {
607 if (acct_ctrl & ACB_PWNOTREQ) {
608 /* this saves the pointer for the caller */
609 *hnd = sampass;
610 return True;
611 } else {
612 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
613 pdb_free_sam(&sampass);
614 return False;
618 /* Get the new lanman hash. */
619 D_P16(lanman_pw, pass2, unenc_new_pw);
621 /* Use this to get the old lanman hash. */
622 D_P16(unenc_new_pw, pass1, unenc_old_pw);
624 /* Check that the two old passwords match. */
625 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
626 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
627 pdb_free_sam(&sampass);
628 return False;
631 /* this saves the pointer for the caller */
632 *hnd = sampass;
633 return True;
636 /***********************************************************
637 Code to change the lanman hashed password.
638 It nulls out the NT hashed password as it will
639 no longer be valid.
640 NOTE this function is designed to be called as root. Check the old password
641 is correct before calling. JRA.
642 ************************************************************/
644 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
646 static uchar null_pw[16];
647 uchar unenc_new_pw[16];
648 BOOL ret;
649 uint16 acct_ctrl;
650 const uint8 *pwd;
652 if (sampass == NULL) {
653 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
654 return False;
657 acct_ctrl = pdb_get_acct_ctrl(sampass);
658 pwd = pdb_get_lanman_passwd(sampass);
660 if (acct_ctrl & ACB_DISABLED) {
661 DEBUG(0,("change_lanman_password: account %s disabled.\n",
662 pdb_get_username(sampass)));
663 return False;
666 if (pwd == NULL) {
667 if (acct_ctrl & ACB_PWNOTREQ) {
668 uchar no_pw[14];
669 memset(no_pw, '\0', 14);
670 E_P16(no_pw, null_pw);
672 /* Get the new lanman hash. */
673 D_P16(null_pw, pass2, unenc_new_pw);
674 } else {
675 DEBUG(0,("change_lanman_password: no lanman password !\n"));
676 return False;
678 } else {
679 /* Get the new lanman hash. */
680 D_P16(pwd, pass2, unenc_new_pw);
683 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
684 return False;
687 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
688 return False; /* We lose the NT hash. Sorry. */
691 if (!pdb_set_pass_changed_now (sampass)) {
692 pdb_free_sam(&sampass);
693 /* Not quite sure what this one qualifies as, but this will do */
694 return False;
697 /* Now flush the sam_passwd struct to persistent storage */
698 ret = pdb_update_sam_account (sampass);
700 return ret;
703 /***********************************************************
704 Code to check and change the OEM hashed password.
705 ************************************************************/
707 NTSTATUS pass_oem_change(char *user,
708 uchar * lmdata, uchar * lmhash,
709 uchar * ntdata, uchar * nthash)
711 fstring new_passwd;
712 SAM_ACCOUNT *sampass = NULL;
713 NTSTATUS nt_status = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
714 &sampass, new_passwd, sizeof(new_passwd));
716 if (!NT_STATUS_IS_OK(nt_status))
717 return nt_status;
719 /* We've already checked the old password here.... */
720 become_root();
721 nt_status = change_oem_password(sampass, NULL, new_passwd, True);
722 unbecome_root();
724 memset(new_passwd, 0, sizeof(new_passwd));
726 pdb_free_sam(&sampass);
728 return nt_status;
731 /***********************************************************
732 Code to check the OEM hashed password.
734 this function ignores the 516 byte nt OEM hashed password
735 but does use the lm OEM password to check the nt hashed-hash.
737 ************************************************************/
739 static NTSTATUS check_oem_password(const char *user,
740 uchar * lmdata, const uchar * lmhash,
741 const uchar * ntdata, const uchar * nthash,
742 SAM_ACCOUNT **hnd, char *new_passwd,
743 int new_passwd_size)
745 static uchar null_pw[16];
746 static uchar null_ntpw[16];
747 SAM_ACCOUNT *sampass = NULL;
748 const uint8 *lanman_pw, *nt_pw;
749 uint16 acct_ctrl;
750 int new_pw_len;
751 uchar new_ntp16[16];
752 uchar unenc_old_ntpw[16];
753 uchar new_p16[16];
754 uchar unenc_old_pw[16];
755 char no_pw[2];
756 BOOL ret;
758 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
760 *hnd = NULL;
762 pdb_init_sam(&sampass);
764 become_root();
765 ret = pdb_getsampwnam(sampass, user);
766 unbecome_root();
768 if (ret == False) {
769 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
770 pdb_free_sam(&sampass);
771 return NT_STATUS_WRONG_PASSWORD;
773 TODO: check what Win2k returns for this:
774 return NT_STATUS_NO_SUCH_USER;
778 acct_ctrl = pdb_get_acct_ctrl(sampass);
780 if (acct_ctrl & ACB_DISABLED) {
781 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
782 pdb_free_sam(&sampass);
783 return NT_STATUS_ACCOUNT_DISABLED;
786 /* construct a null password (in case one is needed */
787 no_pw[0] = 0;
788 no_pw[1] = 0;
789 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
791 /* save pointers to passwords so we don't have to keep looking them up */
792 lanman_pw = pdb_get_lanman_passwd(sampass);
793 nt_pw = pdb_get_nt_passwd(sampass);
795 /* check for null passwords */
796 if (lanman_pw == NULL) {
797 if (!(acct_ctrl & ACB_PWNOTREQ)) {
798 DEBUG(0,("check_oem_password: no lanman password !\n"));
799 pdb_free_sam(&sampass);
800 return NT_STATUS_WRONG_PASSWORD;
804 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
805 if (!(acct_ctrl & ACB_PWNOTREQ)) {
806 DEBUG(0,("check_oem_password: no ntlm password !\n"));
807 pdb_free_sam(&sampass);
808 return NT_STATUS_WRONG_PASSWORD;
813 * Call the hash function to get the new password.
815 SamOEMhash( lmdata, lanman_pw, 516);
818 * The length of the new password is in the last 4 bytes of
819 * the data buffer.
822 new_pw_len = IVAL(lmdata, 512);
824 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
825 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
826 pdb_free_sam(&sampass);
827 return NT_STATUS_WRONG_PASSWORD;
830 if (nt_pass_set) {
832 * nt passwords are in unicode
834 pull_ucs2(NULL, new_passwd,
835 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
836 new_passwd_size, new_pw_len, 0);
837 } else {
838 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
839 new_passwd[new_pw_len] = 0;
843 * To ensure we got the correct new password, hash it and
844 * use it as a key to test the passed old password.
847 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
849 if (!nt_pass_set) {
851 * Now use new_p16 as the key to see if the old
852 * password matches.
854 D_P16(new_p16, lmhash, unenc_old_pw);
856 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
857 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
858 pdb_free_sam(&sampass);
859 return NT_STATUS_WRONG_PASSWORD;
862 #ifdef DEBUG_PASSWORD
863 DEBUG(100,
864 ("check_oem_password: password %s ok\n", new_passwd));
865 #endif
866 *hnd = sampass;
867 return NT_STATUS_OK;
871 * Now use new_p16 as the key to see if the old
872 * password matches.
874 D_P16(new_ntp16, lmhash, unenc_old_pw);
875 D_P16(new_ntp16, nthash, unenc_old_ntpw);
877 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
878 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
879 pdb_free_sam(&sampass);
880 return NT_STATUS_WRONG_PASSWORD;
883 if (memcmp(nt_pw, unenc_old_ntpw, 16)) {
884 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
885 pdb_free_sam(&sampass);
886 return NT_STATUS_WRONG_PASSWORD;
888 #ifdef DEBUG_PASSWORD
889 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
890 #endif
892 *hnd = sampass;
893 return NT_STATUS_OK;
896 /***********************************************************
897 Code to change the oem password. Changes both the lanman
898 and NT hashes. Old_passwd is almost always NULL.
899 NOTE this function is designed to be called as root. Check the old password
900 is correct before calling. JRA.
901 ************************************************************/
903 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
905 struct passwd *pass;
907 BOOL ret;
908 uint32 min_len;
910 if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
911 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
912 pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
913 return NT_STATUS_PASSWORD_RESTRICTION;
916 if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
917 DEBUG(1, ("user %s cannot change password - password too short\n",
918 pdb_get_username(hnd)));
919 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
920 return NT_STATUS_PASSWORD_RESTRICTION;
921 /* return NT_STATUS_PWD_TOO_SHORT; */
924 /* Take the passed information and test it for minimum criteria */
925 /* Minimum password length */
926 if (strlen(new_passwd) < lp_min_passwd_length()) {
927 /* too short, must be at least MINPASSWDLENGTH */
928 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
929 pdb_get_username(hnd), lp_min_passwd_length()));
930 return NT_STATUS_PASSWORD_RESTRICTION;
931 /* return NT_STATUS_PWD_TOO_SHORT; */
934 pass = Get_Pwnam(pdb_get_username(hnd));
935 if (!pass) {
936 DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
939 #ifdef HAVE_WORKING_CRACKLIB
940 if (pass) {
941 /* if we can, become the user to overcome internal cracklib sillyness */
942 if (!push_sec_ctx())
943 return NT_STATUS_UNSUCCESSFUL;
945 set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
946 set_re_uid();
949 if (lp_use_cracklib()) {
950 const char *crack_check_reason;
951 DEBUG(4, ("change_oem_password: Checking password for user [%s]"
952 " against cracklib. \n", pdb_get_username(hnd)));
953 DEBUGADD(4, ("If this is your last message, then something is "
954 "wrong with cracklib, it might be missing it's "
955 "dictionaries at %s\n",
956 CRACKLIB_DICTPATH));
957 dbgflush();
959 crack_check_reason = FascistCheck(new_passwd, (char *)CRACKLIB_DICTPATH);
960 if (crack_check_reason) {
961 DEBUG(1, ("Password Change: user [%s], "
962 "New password failed cracklib test - %s\n",
963 pdb_get_username(hnd), crack_check_reason));
965 /* get back to where we should be */
966 if (pass)
967 pop_sec_ctx();
968 return NT_STATUS_PASSWORD_RESTRICTION;
972 if (pass)
973 pop_sec_ctx();
974 #endif
977 * If unix password sync was requested, attempt to change
978 * the /etc/passwd database first. Return failure if this cannot
979 * be done.
981 * This occurs before the oem change, because we don't want to
982 * update it if chgpasswd failed.
984 * Conditional on lp_unix_password_sync() because we don't want
985 * to touch the unix db unless we have admin permission.
988 if(lp_unix_password_sync() &&
989 !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) {
990 return NT_STATUS_ACCESS_DENIED;
993 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
994 return NT_STATUS_ACCESS_DENIED;
997 /* Now write it into the file. */
998 ret = pdb_update_sam_account (hnd);
1000 if (!ret) {
1001 return NT_STATUS_ACCESS_DENIED;
1004 return NT_STATUS_OK;