First stab at cracklib support (password quality checking) in Samba 3.0
[Samba/gebeck_regimport.git] / source3 / smbd / chgpasswd.c
blob07def7549ebce61a351351590110d0609894822d
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 #include <crack.h>
54 #ifndef HAVE_CRACKLIB_DICTPATH
55 #ifndef CRACKLIB_DICTPATH
56 #define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
57 #endif
58 #endif
60 extern struct passdb_ops pdb_ops;
62 static NTSTATUS check_oem_password(const char *user,
63 uchar * lmdata, const uchar * lmhash,
64 const uchar * ntdata, const uchar * nthash,
65 SAM_ACCOUNT **hnd, char *new_passwd,
66 int new_passwd_size);
68 #if ALLOW_CHANGE_PASSWORD
70 static int findpty(char **slave)
72 int master;
73 static fstring line;
74 DIR *dirp;
75 const char *dpname;
77 #if defined(HAVE_GRANTPT)
78 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
79 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
81 grantpt(master);
82 unlockpt(master);
83 *slave = (char *)ptsname(master);
84 if (*slave == NULL)
86 DEBUG(0,
87 ("findpty: Unable to create master/slave pty pair.\n"));
88 /* Stop fd leak on error. */
89 close(master);
90 return -1;
92 else
94 DEBUG(10,
95 ("findpty: Allocated slave pty %s\n", *slave));
96 return (master);
99 #endif /* HAVE_GRANTPT */
101 fstrcpy(line, "/dev/ptyXX");
103 dirp = opendir("/dev");
104 if (!dirp)
105 return (-1);
106 while ((dpname = readdirname(dirp)) != NULL)
108 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
110 DEBUG(3,
111 ("pty: try to open %s, line was %s\n", dpname,
112 line));
113 line[8] = dpname[3];
114 line[9] = dpname[4];
115 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
117 DEBUG(3, ("pty: opened %s\n", line));
118 line[5] = 't';
119 *slave = line;
120 closedir(dirp);
121 return (master);
125 closedir(dirp);
126 return (-1);
129 static int dochild(int master, const char *slavedev, const struct passwd *pass,
130 const char *passwordprogram, BOOL as_root)
132 int slave;
133 struct termios stermios;
134 gid_t gid;
135 uid_t uid;
137 if (pass == NULL)
139 DEBUG(0,
140 ("dochild: user doesn't exist in the UNIX password database.\n"));
141 return False;
144 gid = pass->pw_gid;
145 uid = pass->pw_uid;
147 gain_root_privilege();
149 /* Start new session - gets rid of controlling terminal. */
150 if (setsid() < 0)
152 DEBUG(3,
153 ("Weirdness, couldn't let go of controlling terminal\n"));
154 return (False);
157 /* Open slave pty and acquire as new controlling terminal. */
158 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
160 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
161 return (False);
163 #ifdef I_PUSH
164 ioctl(slave, I_PUSH, "ptem");
165 ioctl(slave, I_PUSH, "ldterm");
166 #elif defined(TIOCSCTTY)
167 if (ioctl(slave, TIOCSCTTY, 0) < 0)
169 DEBUG(3, ("Error in ioctl call for slave pty\n"));
170 /* return(False); */
172 #endif
174 /* Close master. */
175 close(master);
177 /* Make slave stdin/out/err of child. */
179 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
181 DEBUG(3, ("Could not re-direct stdin\n"));
182 return (False);
184 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
186 DEBUG(3, ("Could not re-direct stdout\n"));
187 return (False);
189 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
191 DEBUG(3, ("Could not re-direct stderr\n"));
192 return (False);
194 if (slave > 2)
195 close(slave);
197 /* Set proper terminal attributes - no echo, canonical input processing,
198 no map NL to CR/NL on output. */
200 if (tcgetattr(0, &stermios) < 0)
202 DEBUG(3,
203 ("could not read default terminal attributes on pty\n"));
204 return (False);
206 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
207 stermios.c_lflag |= ICANON;
208 #ifdef ONLCR
209 stermios.c_oflag &= ~(ONLCR);
210 #endif
211 if (tcsetattr(0, TCSANOW, &stermios) < 0)
213 DEBUG(3, ("could not set attributes of pty\n"));
214 return (False);
217 /* make us completely into the right uid */
218 if (!as_root)
220 become_user_permanently(uid, gid);
223 DEBUG(10,
224 ("Invoking '%s' as password change program.\n",
225 passwordprogram));
227 /* execl() password-change application */
228 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
230 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
231 return (False);
233 return (True);
236 static int expect(int master, char *issue, char *expected)
238 pstring buffer;
239 int attempts, timeout, nread, len;
240 BOOL match = False;
242 for (attempts = 0; attempts < 2; attempts++) {
243 if (!strequal(issue, ".")) {
244 if (lp_passwd_chat_debug())
245 DEBUG(100, ("expect: sending [%s]\n", issue));
247 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
248 DEBUG(2,("expect: (short) write returned %d\n", len ));
249 return False;
253 if (strequal(expected, "."))
254 return True;
256 /* Initial timeout. */
257 timeout = lp_passwd_chat_timeout() * 1000;
258 nread = 0;
259 buffer[nread] = 0;
261 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
262 sizeof(buffer) - nread - 1,
263 timeout)) > 0) {
264 nread += len;
265 buffer[nread] = 0;
268 /* Eat leading/trailing whitespace before match. */
269 pstring str;
270 pstrcpy( str, buffer);
271 trim_char( str, ' ', ' ');
273 if ((match = (unix_wild_match(expected, str) == 0))) {
274 /* Now data has started to return, lower timeout. */
275 timeout = lp_passwd_chat_timeout() * 100;
280 if (lp_passwd_chat_debug())
281 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
282 expected, buffer, match ? "yes" : "no" ));
284 if (match)
285 break;
287 if (len < 0) {
288 DEBUG(2, ("expect: %s\n", strerror(errno)));
289 return False;
293 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
294 return match;
297 static void pwd_sub(char *buf)
299 all_string_sub(buf, "\\n", "\n", 0);
300 all_string_sub(buf, "\\r", "\r", 0);
301 all_string_sub(buf, "\\s", " ", 0);
302 all_string_sub(buf, "\\t", "\t", 0);
305 static int talktochild(int master, const char *seq)
307 int count = 0;
308 fstring issue, expected;
310 fstrcpy(issue, ".");
312 while (next_token(&seq, expected, NULL, sizeof(expected)))
314 pwd_sub(expected);
315 count++;
317 if (!expect(master, issue, expected))
319 DEBUG(3, ("Response %d incorrect\n", count));
320 return False;
323 if (!next_token(&seq, issue, NULL, sizeof(issue)))
324 fstrcpy(issue, ".");
326 pwd_sub(issue);
328 if (!strequal(issue, ".")) {
329 /* we have one final issue to send */
330 fstrcpy(expected, ".");
331 if (!expect(master, issue, expected))
332 return False;
335 return (count > 0);
338 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
339 char *chatsequence, BOOL as_root)
341 char *slavedev;
342 int master;
343 pid_t pid, wpid;
344 int wstat;
345 BOOL chstat = False;
347 if (pass == NULL) {
348 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
349 return False;
352 /* allocate a pseudo-terminal device */
353 if ((master = findpty(&slavedev)) < 0) {
354 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
355 return (False);
359 * We need to temporarily stop CatchChild from eating
360 * SIGCLD signals as it also eats the exit status code. JRA.
363 CatchChildLeaveStatus();
365 if ((pid = sys_fork()) < 0) {
366 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
367 close(master);
368 CatchChild();
369 return (False);
372 /* we now have a pty */
373 if (pid > 0) { /* This is the parent process */
374 if ((chstat = talktochild(master, chatsequence)) == False) {
375 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
376 kill(pid, SIGKILL); /* be sure to end this process */
379 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
380 if (errno == EINTR) {
381 errno = 0;
382 continue;
384 break;
387 if (wpid < 0) {
388 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
389 close(master);
390 CatchChild();
391 return (False);
395 * Go back to ignoring children.
397 CatchChild();
399 close(master);
401 if (pid != wpid) {
402 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
403 return (False);
405 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
406 DEBUG(3, ("chat_with_program: The process exited with status %d \
407 while we were waiting\n", WEXITSTATUS(wstat)));
408 return (False);
410 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
411 else if (WIFSIGNALLED(wstat)) {
412 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
413 while we were waiting\n", WTERMSIG(wstat)));
414 return (False);
416 #endif
417 } else {
418 /* CHILD */
421 * Lose any oplock capabilities.
423 oplock_set_capability(False, False);
425 /* make sure it doesn't freeze */
426 alarm(20);
428 if (as_root)
429 become_root();
431 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
432 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
433 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
435 if (as_root)
436 unbecome_root();
439 * The child should never return from dochild() ....
442 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
443 exit(1);
446 if (chstat)
447 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
448 (chstat ? "" : "un"), pass->pw_name));
449 return (chstat);
452 BOOL chgpasswd(const char *name, const struct passwd *pass,
453 const char *oldpass, const char *newpass, BOOL as_root)
455 pstring passwordprogram;
456 pstring chatsequence;
457 size_t i;
458 size_t len;
460 if (!oldpass) {
461 oldpass = "";
464 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
466 #if DEBUG_PASSWORD
467 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
468 #endif
470 /* Take the passed information and test it for minimum criteria */
472 /* Password is same as old password */
473 if (strcmp(oldpass, newpass) == 0) {
474 /* don't allow same password */
475 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
476 return (False); /* inform the user */
480 * Check the old and new passwords don't contain any control
481 * characters.
484 len = strlen(oldpass);
485 for (i = 0; i < len; i++) {
486 if (iscntrl((int)oldpass[i])) {
487 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
488 return False;
492 len = strlen(newpass);
493 for (i = 0; i < len; i++) {
494 if (iscntrl((int)newpass[i])) {
495 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
496 return False;
500 #ifdef WITH_PAM
501 if (lp_pam_password_change()) {
502 BOOL ret;
504 if (as_root)
505 become_root();
507 if (pass) {
508 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
509 } else {
510 ret = smb_pam_passchange(name, oldpass, newpass);
513 if (as_root)
514 unbecome_root();
516 return ret;
518 #endif
520 /* A non-PAM password change just doen't make sense without a valid local user */
522 if (pass == NULL) {
523 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
524 return False;
527 pstrcpy(passwordprogram, lp_passwd_program());
528 pstrcpy(chatsequence, lp_passwd_chat());
530 if (!*chatsequence) {
531 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
532 return (False);
535 if (!*passwordprogram) {
536 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
537 return (False);
540 if (as_root) {
541 /* The password program *must* contain the user name to work. Fail if not. */
542 if (strstr(passwordprogram, "%u") == NULL) {
543 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
544 the string %%u, and the given string %s does not.\n", passwordprogram ));
545 return False;
549 pstring_sub(passwordprogram, "%u", name);
550 /* note that we do NOT substitute the %o and %n in the password program
551 as this would open up a security hole where the user could use
552 a new password containing shell escape characters */
554 pstring_sub(chatsequence, "%u", name);
555 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
556 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
557 return (chat_with_program
558 (passwordprogram, pass, chatsequence, as_root));
561 #else /* ALLOW_CHANGE_PASSWORD */
563 BOOL chgpasswd(const char *name, const struct passwd *pass,
564 const char *oldpass, const char *newpass, BOOL as_root)
566 DEBUG(0, ("chgpasswd: Password changing not compiled in (user=%s)\n", name));
567 return (False);
569 #endif /* ALLOW_CHANGE_PASSWORD */
571 /***********************************************************
572 Code to check the lanman hashed password.
573 ************************************************************/
575 BOOL check_lanman_password(char *user, uchar * pass1,
576 uchar * pass2, SAM_ACCOUNT **hnd)
578 uchar unenc_new_pw[16];
579 uchar unenc_old_pw[16];
580 SAM_ACCOUNT *sampass = NULL;
581 uint16 acct_ctrl;
582 const uint8 *lanman_pw;
583 BOOL ret;
585 become_root();
586 ret = pdb_getsampwnam(sampass, user);
587 unbecome_root();
589 if (ret == False) {
590 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
591 pdb_free_sam(&sampass);
592 return False;
595 acct_ctrl = pdb_get_acct_ctrl (sampass);
596 lanman_pw = pdb_get_lanman_passwd (sampass);
598 if (acct_ctrl & ACB_DISABLED) {
599 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
600 pdb_free_sam(&sampass);
601 return False;
604 if (lanman_pw == NULL) {
605 if (acct_ctrl & ACB_PWNOTREQ) {
606 /* this saves the pointer for the caller */
607 *hnd = sampass;
608 return True;
609 } else {
610 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
611 pdb_free_sam(&sampass);
612 return False;
616 /* Get the new lanman hash. */
617 D_P16(lanman_pw, pass2, unenc_new_pw);
619 /* Use this to get the old lanman hash. */
620 D_P16(unenc_new_pw, pass1, unenc_old_pw);
622 /* Check that the two old passwords match. */
623 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
624 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
625 pdb_free_sam(&sampass);
626 return False;
629 /* this saves the pointer for the caller */
630 *hnd = sampass;
631 return True;
634 /***********************************************************
635 Code to change the lanman hashed password.
636 It nulls out the NT hashed password as it will
637 no longer be valid.
638 NOTE this function is designed to be called as root. Check the old password
639 is correct before calling. JRA.
640 ************************************************************/
642 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
644 static uchar null_pw[16];
645 uchar unenc_new_pw[16];
646 BOOL ret;
647 uint16 acct_ctrl;
648 const uint8 *pwd;
650 if (sampass == NULL) {
651 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
652 return False;
655 acct_ctrl = pdb_get_acct_ctrl(sampass);
656 pwd = pdb_get_lanman_passwd(sampass);
658 if (acct_ctrl & ACB_DISABLED) {
659 DEBUG(0,("change_lanman_password: account %s disabled.\n",
660 pdb_get_username(sampass)));
661 return False;
664 if (pwd == NULL) {
665 if (acct_ctrl & ACB_PWNOTREQ) {
666 uchar no_pw[14];
667 memset(no_pw, '\0', 14);
668 E_P16(no_pw, null_pw);
670 /* Get the new lanman hash. */
671 D_P16(null_pw, pass2, unenc_new_pw);
672 } else {
673 DEBUG(0,("change_lanman_password: no lanman password !\n"));
674 return False;
676 } else {
677 /* Get the new lanman hash. */
678 D_P16(pwd, pass2, unenc_new_pw);
681 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
682 return False;
685 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
686 return False; /* We lose the NT hash. Sorry. */
689 if (!pdb_set_pass_changed_now (sampass)) {
690 pdb_free_sam(&sampass);
691 /* Not quite sure what this one qualifies as, but this will do */
692 return False;
695 /* Now flush the sam_passwd struct to persistent storage */
696 ret = pdb_update_sam_account (sampass);
698 return ret;
701 /***********************************************************
702 Code to check and change the OEM hashed password.
703 ************************************************************/
705 NTSTATUS pass_oem_change(char *user,
706 uchar * lmdata, uchar * lmhash,
707 uchar * ntdata, uchar * nthash)
709 fstring new_passwd;
710 SAM_ACCOUNT *sampass = NULL;
711 NTSTATUS nt_status = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
712 &sampass, new_passwd, sizeof(new_passwd));
714 if (!NT_STATUS_IS_OK(nt_status))
715 return nt_status;
717 /* We've already checked the old password here.... */
718 become_root();
719 nt_status = change_oem_password(sampass, NULL, new_passwd, True);
720 unbecome_root();
722 memset(new_passwd, 0, sizeof(new_passwd));
724 pdb_free_sam(&sampass);
726 return nt_status;
729 /***********************************************************
730 Code to check the OEM hashed password.
732 this function ignores the 516 byte nt OEM hashed password
733 but does use the lm OEM password to check the nt hashed-hash.
735 ************************************************************/
737 static NTSTATUS check_oem_password(const char *user,
738 uchar * lmdata, const uchar * lmhash,
739 const uchar * ntdata, const uchar * nthash,
740 SAM_ACCOUNT **hnd, char *new_passwd,
741 int new_passwd_size)
743 static uchar null_pw[16];
744 static uchar null_ntpw[16];
745 SAM_ACCOUNT *sampass = NULL;
746 const uint8 *lanman_pw, *nt_pw;
747 uint16 acct_ctrl;
748 int new_pw_len;
749 uchar new_ntp16[16];
750 uchar unenc_old_ntpw[16];
751 uchar new_p16[16];
752 uchar unenc_old_pw[16];
753 char no_pw[2];
754 BOOL ret;
756 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
758 *hnd = NULL;
760 pdb_init_sam(&sampass);
762 become_root();
763 ret = pdb_getsampwnam(sampass, user);
764 unbecome_root();
766 if (ret == False) {
767 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
768 pdb_free_sam(&sampass);
769 return NT_STATUS_WRONG_PASSWORD;
771 TODO: check what Win2k returns for this:
772 return NT_STATUS_NO_SUCH_USER;
776 acct_ctrl = pdb_get_acct_ctrl(sampass);
778 if (acct_ctrl & ACB_DISABLED) {
779 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
780 pdb_free_sam(&sampass);
781 return NT_STATUS_ACCOUNT_DISABLED;
784 /* construct a null password (in case one is needed */
785 no_pw[0] = 0;
786 no_pw[1] = 0;
787 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
789 /* save pointers to passwords so we don't have to keep looking them up */
790 lanman_pw = pdb_get_lanman_passwd(sampass);
791 nt_pw = pdb_get_nt_passwd(sampass);
793 /* check for null passwords */
794 if (lanman_pw == NULL) {
795 if (!(acct_ctrl & ACB_PWNOTREQ)) {
796 DEBUG(0,("check_oem_password: no lanman password !\n"));
797 pdb_free_sam(&sampass);
798 return NT_STATUS_WRONG_PASSWORD;
802 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
803 if (!(acct_ctrl & ACB_PWNOTREQ)) {
804 DEBUG(0,("check_oem_password: no ntlm password !\n"));
805 pdb_free_sam(&sampass);
806 return NT_STATUS_WRONG_PASSWORD;
811 * Call the hash function to get the new password.
813 SamOEMhash( lmdata, lanman_pw, 516);
816 * The length of the new password is in the last 4 bytes of
817 * the data buffer.
820 new_pw_len = IVAL(lmdata, 512);
822 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
823 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
824 pdb_free_sam(&sampass);
825 return NT_STATUS_WRONG_PASSWORD;
828 if (nt_pass_set) {
830 * nt passwords are in unicode
832 pull_ucs2(NULL, new_passwd,
833 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
834 new_passwd_size, new_pw_len, 0);
835 } else {
836 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
837 new_passwd[new_pw_len] = 0;
841 * To ensure we got the correct new password, hash it and
842 * use it as a key to test the passed old password.
845 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
847 if (!nt_pass_set) {
849 * Now use new_p16 as the key to see if the old
850 * password matches.
852 D_P16(new_p16, lmhash, unenc_old_pw);
854 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
855 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
856 pdb_free_sam(&sampass);
857 return NT_STATUS_WRONG_PASSWORD;
860 #ifdef DEBUG_PASSWORD
861 DEBUG(100,
862 ("check_oem_password: password %s ok\n", new_passwd));
863 #endif
864 *hnd = sampass;
865 return NT_STATUS_OK;
869 * Now use new_p16 as the key to see if the old
870 * password matches.
872 D_P16(new_ntp16, lmhash, unenc_old_pw);
873 D_P16(new_ntp16, nthash, unenc_old_ntpw);
875 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
876 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
877 pdb_free_sam(&sampass);
878 return NT_STATUS_WRONG_PASSWORD;
881 if (memcmp(nt_pw, unenc_old_ntpw, 16)) {
882 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
883 pdb_free_sam(&sampass);
884 return NT_STATUS_WRONG_PASSWORD;
886 #ifdef DEBUG_PASSWORD
887 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
888 #endif
890 *hnd = sampass;
891 return NT_STATUS_OK;
894 /***********************************************************
895 Code to change the oem password. Changes both the lanman
896 and NT hashes. Old_passwd is almost always NULL.
897 NOTE this function is designed to be called as root. Check the old password
898 is correct before calling. JRA.
899 ************************************************************/
901 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
903 struct passwd *pass;
905 BOOL ret;
906 uint32 min_len;
908 if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
909 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
910 pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
911 return NT_STATUS_PASSWORD_RESTRICTION;
914 if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
915 DEBUG(1, ("user %s cannot change password - password too short\n",
916 pdb_get_username(hnd)));
917 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
918 return NT_STATUS_PASSWORD_RESTRICTION;
919 /* return NT_STATUS_PWD_TOO_SHORT; */
922 /* Take the passed information and test it for minimum criteria */
923 /* Minimum password length */
924 if (strlen(new_passwd) < lp_min_passwd_length()) {
925 /* too short, must be at least MINPASSWDLENGTH */
926 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
927 pdb_get_username(hnd), lp_min_passwd_length()));
928 return NT_STATUS_PASSWORD_RESTRICTION;
929 /* return NT_STATUS_PWD_TOO_SHORT; */
932 pass = Get_Pwnam(pdb_get_username(hnd));
933 if (!pass) {
934 DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
937 #ifdef HAVE_WORKING_CRACKLIB
938 if (pass) {
939 /* if we can, become the user to overcome internal cracklib sillyness */
940 if (!push_sec_ctx())
941 return NT_STATUS_UNSUCCESSFUL;
943 set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
944 set_re_uid();
947 if (lp_use_cracklib()) {
948 const char *crack_check_reason;
949 DEBUG(4, ("change_oem_password: Checking password for user [%s]"
950 " against cracklib. \n", pdb_get_username(hnd)));
951 DEBUGADD(4, ("If this is your last message, then something is "
952 "wrong with cracklib, it might be missing it's "
953 "dictionaries at %s\n",
954 CRACKLIB_DICTPATH));
955 dbgflush();
957 crack_check_reason = FascistCheck(new_passwd, (char *)CRACKLIB_DICTPATH);
958 if (crack_check_reason) {
959 DEBUG(1, ("Password Change: user [%s], "
960 "New password failed cracklib test - %s\n",
961 pdb_get_username(hnd), crack_check_reason));
963 /* get back to where we should be */
964 if (pass)
965 pop_sec_ctx();
966 return NT_STATUS_PASSWORD_RESTRICTION;
970 if (pass)
971 pop_sec_ctx();
972 #endif
975 * If unix password sync was requested, attempt to change
976 * the /etc/passwd database first. Return failure if this cannot
977 * be done.
979 * This occurs before the oem change, because we don't want to
980 * update it if chgpasswd failed.
982 * Conditional on lp_unix_password_sync() because we don't want
983 * to touch the unix db unless we have admin permission.
986 if(lp_unix_password_sync() &&
987 !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) {
988 return NT_STATUS_ACCESS_DENIED;
991 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
992 return NT_STATUS_ACCESS_DENIED;
995 /* Now write it into the file. */
996 ret = pdb_update_sam_account (hnd);
998 if (!ret) {
999 return NT_STATUS_ACCESS_DENIED;
1002 return NT_STATUS_OK;