Volker's fix for bug #668. Change the \n after the password prompt to go
[Samba/gbeck.git] / source / smbd / chgpasswd.c
blobd99570ff7c0125acbdf5b3c26f3018f4300a2b59
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 extern struct passdb_ops pdb_ops;
54 static NTSTATUS check_oem_password(const char *user,
55 uchar * lmdata, const uchar * lmhash,
56 const uchar * ntdata, const uchar * nthash,
57 SAM_ACCOUNT **hnd, char *new_passwd,
58 int new_passwd_size);
60 #if ALLOW_CHANGE_PASSWORD
62 static int findpty(char **slave)
64 int master;
65 static fstring line;
66 DIR *dirp;
67 const char *dpname;
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)
73 grantpt(master);
74 unlockpt(master);
75 *slave = (char *)ptsname(master);
76 if (*slave == NULL)
78 DEBUG(0,
79 ("findpty: Unable to create master/slave pty pair.\n"));
80 /* Stop fd leak on error. */
81 close(master);
82 return -1;
84 else
86 DEBUG(10,
87 ("findpty: Allocated slave pty %s\n", *slave));
88 return (master);
91 #endif /* HAVE_GRANTPT */
93 fstrcpy(line, "/dev/ptyXX");
95 dirp = opendir("/dev");
96 if (!dirp)
97 return (-1);
98 while ((dpname = readdirname(dirp)) != NULL)
100 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
102 DEBUG(3,
103 ("pty: try to open %s, line was %s\n", dpname,
104 line));
105 line[8] = dpname[3];
106 line[9] = dpname[4];
107 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
109 DEBUG(3, ("pty: opened %s\n", line));
110 line[5] = 't';
111 *slave = line;
112 closedir(dirp);
113 return (master);
117 closedir(dirp);
118 return (-1);
121 static int dochild(int master, const char *slavedev, const struct passwd *pass,
122 const char *passwordprogram, BOOL as_root)
124 int slave;
125 struct termios stermios;
126 gid_t gid;
127 uid_t uid;
129 if (pass == NULL)
131 DEBUG(0,
132 ("dochild: user doesn't exist in the UNIX password database.\n"));
133 return False;
136 gid = pass->pw_gid;
137 uid = pass->pw_uid;
139 gain_root_privilege();
141 /* Start new session - gets rid of controlling terminal. */
142 if (setsid() < 0)
144 DEBUG(3,
145 ("Weirdness, couldn't let go of controlling terminal\n"));
146 return (False);
149 /* Open slave pty and acquire as new controlling terminal. */
150 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
152 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
153 return (False);
155 #ifdef I_PUSH
156 ioctl(slave, I_PUSH, "ptem");
157 ioctl(slave, I_PUSH, "ldterm");
158 #elif defined(TIOCSCTTY)
159 if (ioctl(slave, TIOCSCTTY, 0) < 0)
161 DEBUG(3, ("Error in ioctl call for slave pty\n"));
162 /* return(False); */
164 #endif
166 /* Close master. */
167 close(master);
169 /* Make slave stdin/out/err of child. */
171 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
173 DEBUG(3, ("Could not re-direct stdin\n"));
174 return (False);
176 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
178 DEBUG(3, ("Could not re-direct stdout\n"));
179 return (False);
181 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
183 DEBUG(3, ("Could not re-direct stderr\n"));
184 return (False);
186 if (slave > 2)
187 close(slave);
189 /* Set proper terminal attributes - no echo, canonical input processing,
190 no map NL to CR/NL on output. */
192 if (tcgetattr(0, &stermios) < 0)
194 DEBUG(3,
195 ("could not read default terminal attributes on pty\n"));
196 return (False);
198 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
199 stermios.c_lflag |= ICANON;
200 #ifdef ONLCR
201 stermios.c_oflag &= ~(ONLCR);
202 #endif
203 if (tcsetattr(0, TCSANOW, &stermios) < 0)
205 DEBUG(3, ("could not set attributes of pty\n"));
206 return (False);
209 /* make us completely into the right uid */
210 if (!as_root)
212 become_user_permanently(uid, gid);
215 DEBUG(10,
216 ("Invoking '%s' as password change program.\n",
217 passwordprogram));
219 /* execl() password-change application */
220 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
222 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
223 return (False);
225 return (True);
228 static int expect(int master, char *issue, char *expected)
230 pstring buffer;
231 int attempts, timeout, nread, len;
232 BOOL match = False;
234 for (attempts = 0; attempts < 2; attempts++) {
235 if (!strequal(issue, ".")) {
236 if (lp_passwd_chat_debug())
237 DEBUG(100, ("expect: sending [%s]\n", issue));
239 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
240 DEBUG(2,("expect: (short) write returned %d\n", len ));
241 return False;
245 if (strequal(expected, "."))
246 return True;
248 timeout = 2000;
249 nread = 0;
250 buffer[nread] = 0;
252 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
253 sizeof(buffer) - nread - 1,
254 timeout)) > 0) {
255 nread += len;
256 buffer[nread] = 0;
259 /* Eat leading/trailing whitespace before match. */
260 pstring str;
261 pstrcpy( str, buffer);
262 trim_char( str, ' ', ' ');
264 if ((match = (unix_wild_match(expected, str) == 0)))
265 timeout = 200;
269 if (lp_passwd_chat_debug())
270 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
271 expected, buffer, match ? "yes" : "no" ));
273 if (match)
274 break;
276 if (len < 0) {
277 DEBUG(2, ("expect: %s\n", strerror(errno)));
278 return False;
282 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
283 return match;
286 static void pwd_sub(char *buf)
288 all_string_sub(buf, "\\n", "\n", 0);
289 all_string_sub(buf, "\\r", "\r", 0);
290 all_string_sub(buf, "\\s", " ", 0);
291 all_string_sub(buf, "\\t", "\t", 0);
294 static int talktochild(int master, const char *seq)
296 int count = 0;
297 fstring issue, expected;
299 fstrcpy(issue, ".");
301 while (next_token(&seq, expected, NULL, sizeof(expected)))
303 pwd_sub(expected);
304 count++;
306 if (!expect(master, issue, expected))
308 DEBUG(3, ("Response %d incorrect\n", count));
309 return False;
312 if (!next_token(&seq, issue, NULL, sizeof(issue)))
313 fstrcpy(issue, ".");
315 pwd_sub(issue);
317 if (!strequal(issue, ".")) {
318 /* we have one final issue to send */
319 fstrcpy(expected, ".");
320 if (!expect(master, issue, expected))
321 return False;
324 return (count > 0);
327 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
328 char *chatsequence, BOOL as_root)
330 char *slavedev;
331 int master;
332 pid_t pid, wpid;
333 int wstat;
334 BOOL chstat = False;
336 if (pass == NULL) {
337 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
338 return False;
341 /* allocate a pseudo-terminal device */
342 if ((master = findpty(&slavedev)) < 0) {
343 DEBUG(3, ("Cannot Allocate pty for password change: %s\n", pass->pw_name));
344 return (False);
348 * We need to temporarily stop CatchChild from eating
349 * SIGCLD signals as it also eats the exit status code. JRA.
352 CatchChildLeaveStatus();
354 if ((pid = sys_fork()) < 0) {
355 DEBUG(3, ("Cannot fork() child for password change: %s\n", pass->pw_name));
356 close(master);
357 CatchChild();
358 return (False);
361 /* we now have a pty */
362 if (pid > 0) { /* This is the parent process */
363 if ((chstat = talktochild(master, chatsequence)) == False) {
364 DEBUG(3, ("Child failed to change password: %s\n", pass->pw_name));
365 kill(pid, SIGKILL); /* be sure to end this process */
368 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
369 if (errno == EINTR) {
370 errno = 0;
371 continue;
373 break;
376 if (wpid < 0) {
377 DEBUG(3, ("The process is no longer waiting!\n\n"));
378 close(master);
379 CatchChild();
380 return (False);
384 * Go back to ignoring children.
386 CatchChild();
388 close(master);
390 if (pid != wpid) {
391 DEBUG(3, ("We were waiting for the wrong process ID\n"));
392 return (False);
394 if (WIFEXITED(wstat) == 0) {
395 DEBUG(3, ("The process exited while we were waiting\n"));
396 return (False);
398 if (WEXITSTATUS(wstat) != 0) {
399 DEBUG(3, ("The status of the process exiting was %d\n",
400 wstat));
401 return (False);
404 } else {
405 /* CHILD */
408 * Lose any oplock capabilities.
410 oplock_set_capability(False, False);
412 /* make sure it doesn't freeze */
413 alarm(20);
415 if (as_root)
416 become_root();
418 DEBUG(3, ("Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
419 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
420 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
422 if (as_root)
423 unbecome_root();
426 * The child should never return from dochild() ....
429 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
430 exit(1);
433 if (chstat)
434 DEBUG(3, ("Password change %ssuccessful for user %s\n",
435 (chstat ? "" : "un"), pass->pw_name));
436 return (chstat);
439 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
441 pstring passwordprogram;
442 pstring chatsequence;
443 size_t i;
444 size_t len;
446 struct passwd *pass;
448 if (!name) {
449 DEBUG(1, ("NULL username specfied to chgpasswd()!\n"));
452 pass = Get_Pwnam(name);
453 if (!pass) {
454 DEBUG(1, ("Username does not exist in system passwd!\n"));
455 return False;
458 if (!oldpass) {
459 oldpass = "";
462 DEBUG(3, ("Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
464 #if DEBUG_PASSWORD
465 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
466 #endif
468 /* Take the passed information and test it for minimum criteria */
469 /* Minimum password length */
470 if (strlen(newpass) < lp_min_passwd_length()) {
471 /* too short, must be at least MINPASSWDLENGTH */
472 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
473 name, lp_min_passwd_length()));
474 return (False); /* inform the user */
477 /* Password is same as old password */
478 if (strcmp(oldpass, newpass) == 0) {
479 /* don't allow same password */
480 DEBUG(2, ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
481 return (False); /* inform the user */
485 * Check the old and new passwords don't contain any control
486 * characters.
489 len = strlen(oldpass);
490 for (i = 0; i < len; i++) {
491 if (iscntrl((int)oldpass[i])) {
492 DEBUG(0, ("chat_with_program: oldpass contains control characters (disallowed).\n"));
493 return False;
497 len = strlen(newpass);
498 for (i = 0; i < len; i++) {
499 if (iscntrl((int)newpass[i])) {
500 DEBUG(0, ("chat_with_program: newpass contains control characters (disallowed).\n"));
501 return False;
505 #ifdef WITH_PAM
506 if (lp_pam_password_change()) {
507 BOOL ret;
509 if (as_root)
510 become_root();
512 if (pass) {
513 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
514 } else {
515 ret = smb_pam_passchange(name, oldpass, newpass);
518 if (as_root)
519 unbecome_root();
521 return ret;
523 #endif
525 /* A non-PAM password change just doen't make sense without a valid local user */
527 if (pass == NULL) {
528 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
529 return False;
532 pstrcpy(passwordprogram, lp_passwd_program());
533 pstrcpy(chatsequence, lp_passwd_chat());
535 if (!*chatsequence) {
536 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
537 return (False);
540 if (!*passwordprogram) {
541 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
542 return (False);
545 if (as_root) {
546 /* The password program *must* contain the user name to work. Fail if not. */
547 if (strstr(passwordprogram, "%u") == NULL) {
548 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
549 the string %%u, and the given string %s does not.\n", passwordprogram ));
550 return False;
554 pstring_sub(passwordprogram, "%u", name);
555 /* note that we do NOT substitute the %o and %n in the password program
556 as this would open up a security hole where the user could use
557 a new password containing shell escape characters */
559 pstring_sub(chatsequence, "%u", name);
560 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
561 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
562 return (chat_with_program
563 (passwordprogram, pass, chatsequence, as_root));
566 #else /* ALLOW_CHANGE_PASSWORD */
568 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
570 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
571 return (False);
573 #endif /* ALLOW_CHANGE_PASSWORD */
575 /***********************************************************
576 Code to check the lanman hashed password.
577 ************************************************************/
579 BOOL check_lanman_password(char *user, uchar * pass1,
580 uchar * pass2, SAM_ACCOUNT **hnd)
582 uchar unenc_new_pw[16];
583 uchar unenc_old_pw[16];
584 SAM_ACCOUNT *sampass = NULL;
585 uint16 acct_ctrl;
586 const uint8 *lanman_pw;
587 BOOL ret;
589 become_root();
590 ret = pdb_getsampwnam(sampass, user);
591 unbecome_root();
593 if (ret == False) {
594 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
595 pdb_free_sam(&sampass);
596 return False;
599 acct_ctrl = pdb_get_acct_ctrl (sampass);
600 lanman_pw = pdb_get_lanman_passwd (sampass);
602 if (acct_ctrl & ACB_DISABLED) {
603 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
604 pdb_free_sam(&sampass);
605 return False;
608 if (lanman_pw == NULL) {
609 if (acct_ctrl & ACB_PWNOTREQ) {
610 /* this saves the pointer for the caller */
611 *hnd = sampass;
612 return True;
613 } else {
614 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
615 pdb_free_sam(&sampass);
616 return False;
620 /* Get the new lanman hash. */
621 D_P16(lanman_pw, pass2, unenc_new_pw);
623 /* Use this to get the old lanman hash. */
624 D_P16(unenc_new_pw, pass1, unenc_old_pw);
626 /* Check that the two old passwords match. */
627 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
628 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
629 pdb_free_sam(&sampass);
630 return False;
633 /* this saves the pointer for the caller */
634 *hnd = sampass;
635 return True;
638 /***********************************************************
639 Code to change the lanman hashed password.
640 It nulls out the NT hashed password as it will
641 no longer be valid.
642 NOTE this function is designed to be called as root. Check the old password
643 is correct before calling. JRA.
644 ************************************************************/
646 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
648 static uchar null_pw[16];
649 uchar unenc_new_pw[16];
650 BOOL ret;
651 uint16 acct_ctrl;
652 const uint8 *pwd;
654 if (sampass == NULL) {
655 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
656 return False;
659 acct_ctrl = pdb_get_acct_ctrl(sampass);
660 pwd = pdb_get_lanman_passwd(sampass);
662 if (acct_ctrl & ACB_DISABLED) {
663 DEBUG(0,("change_lanman_password: account %s disabled.\n",
664 pdb_get_username(sampass)));
665 return False;
668 if (pwd == NULL) {
669 if (acct_ctrl & ACB_PWNOTREQ) {
670 uchar no_pw[14];
671 memset(no_pw, '\0', 14);
672 E_P16(no_pw, null_pw);
674 /* Get the new lanman hash. */
675 D_P16(null_pw, pass2, unenc_new_pw);
676 } else {
677 DEBUG(0,("change_lanman_password: no lanman password !\n"));
678 return False;
680 } else {
681 /* Get the new lanman hash. */
682 D_P16(pwd, pass2, unenc_new_pw);
685 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
686 return False;
689 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
690 return False; /* We lose the NT hash. Sorry. */
693 if (!pdb_set_pass_changed_now (sampass)) {
694 pdb_free_sam(&sampass);
695 /* Not quite sure what this one qualifies as, but this will do */
696 return False;
699 /* Now flush the sam_passwd struct to persistent storage */
700 ret = pdb_update_sam_account (sampass);
702 return ret;
705 /***********************************************************
706 Code to check and change the OEM hashed password.
707 ************************************************************/
709 NTSTATUS pass_oem_change(char *user,
710 uchar * lmdata, uchar * lmhash,
711 uchar * ntdata, uchar * nthash)
713 fstring new_passwd;
714 SAM_ACCOUNT *sampass = NULL;
715 NTSTATUS nt_status = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
716 &sampass, new_passwd, sizeof(new_passwd));
718 if (!NT_STATUS_IS_OK(nt_status))
719 return nt_status;
721 /* We've already checked the old password here.... */
722 become_root();
723 nt_status = change_oem_password(sampass, NULL, new_passwd, True);
724 unbecome_root();
726 memset(new_passwd, 0, sizeof(new_passwd));
728 pdb_free_sam(&sampass);
730 return nt_status;
733 /***********************************************************
734 Code to check the OEM hashed password.
736 this function ignores the 516 byte nt OEM hashed password
737 but does use the lm OEM password to check the nt hashed-hash.
739 ************************************************************/
741 static NTSTATUS check_oem_password(const char *user,
742 uchar * lmdata, const uchar * lmhash,
743 const uchar * ntdata, const uchar * nthash,
744 SAM_ACCOUNT **hnd, char *new_passwd,
745 int new_passwd_size)
747 static uchar null_pw[16];
748 static uchar null_ntpw[16];
749 SAM_ACCOUNT *sampass = NULL;
750 const uint8 *lanman_pw, *nt_pw;
751 uint16 acct_ctrl;
752 int new_pw_len;
753 uchar new_ntp16[16];
754 uchar unenc_old_ntpw[16];
755 uchar new_p16[16];
756 uchar unenc_old_pw[16];
757 char no_pw[2];
758 BOOL ret;
760 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
762 *hnd = NULL;
764 pdb_init_sam(&sampass);
766 become_root();
767 ret = pdb_getsampwnam(sampass, user);
768 unbecome_root();
770 if (ret == False) {
771 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
772 pdb_free_sam(&sampass);
773 return NT_STATUS_WRONG_PASSWORD;
775 TODO: check what Win2k returns for this:
776 return NT_STATUS_NO_SUCH_USER;
780 acct_ctrl = pdb_get_acct_ctrl(sampass);
782 if (acct_ctrl & ACB_DISABLED) {
783 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
784 pdb_free_sam(&sampass);
785 return NT_STATUS_ACCOUNT_DISABLED;
788 /* construct a null password (in case one is needed */
789 no_pw[0] = 0;
790 no_pw[1] = 0;
791 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
793 /* save pointers to passwords so we don't have to keep looking them up */
794 lanman_pw = pdb_get_lanman_passwd(sampass);
795 nt_pw = pdb_get_nt_passwd(sampass);
797 /* check for null passwords */
798 if (lanman_pw == NULL) {
799 if (!(acct_ctrl & ACB_PWNOTREQ)) {
800 DEBUG(0,("check_oem_password: no lanman password !\n"));
801 pdb_free_sam(&sampass);
802 return NT_STATUS_WRONG_PASSWORD;
806 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
807 if (!(acct_ctrl & ACB_PWNOTREQ)) {
808 DEBUG(0,("check_oem_password: no ntlm password !\n"));
809 pdb_free_sam(&sampass);
810 return NT_STATUS_WRONG_PASSWORD;
815 * Call the hash function to get the new password.
817 SamOEMhash( lmdata, lanman_pw, 516);
820 * The length of the new password is in the last 4 bytes of
821 * the data buffer.
824 new_pw_len = IVAL(lmdata, 512);
826 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
827 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
828 pdb_free_sam(&sampass);
829 return NT_STATUS_WRONG_PASSWORD;
832 if (nt_pass_set) {
834 * nt passwords are in unicode
836 pull_ucs2(NULL, new_passwd,
837 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
838 new_passwd_size, new_pw_len, 0);
839 } else {
840 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
841 new_passwd[new_pw_len] = 0;
845 * To ensure we got the correct new password, hash it and
846 * use it as a key to test the passed old password.
849 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
851 if (!nt_pass_set) {
853 * Now use new_p16 as the key to see if the old
854 * password matches.
856 D_P16(new_p16, lmhash, unenc_old_pw);
858 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
859 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
860 pdb_free_sam(&sampass);
861 return NT_STATUS_WRONG_PASSWORD;
864 #ifdef DEBUG_PASSWORD
865 DEBUG(100,
866 ("check_oem_password: password %s ok\n", new_passwd));
867 #endif
868 *hnd = sampass;
869 return NT_STATUS_OK;
873 * Now use new_p16 as the key to see if the old
874 * password matches.
876 D_P16(new_ntp16, lmhash, unenc_old_pw);
877 D_P16(new_ntp16, nthash, unenc_old_ntpw);
879 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
880 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
881 pdb_free_sam(&sampass);
882 return NT_STATUS_WRONG_PASSWORD;
885 if (memcmp(nt_pw, unenc_old_ntpw, 16)) {
886 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
887 pdb_free_sam(&sampass);
888 return NT_STATUS_WRONG_PASSWORD;
890 #ifdef DEBUG_PASSWORD
891 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
892 #endif
894 *hnd = sampass;
895 return NT_STATUS_OK;
898 /***********************************************************
899 Code to change the oem password. Changes both the lanman
900 and NT hashes. Old_passwd is almost always NULL.
901 NOTE this function is designed to be called as root. Check the old password
902 is correct before calling. JRA.
903 ************************************************************/
905 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
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 /* TODO: Add cracklib support here */
937 * If unix password sync was requested, attempt to change
938 * the /etc/passwd database first. Return failure if this cannot
939 * be done.
941 * This occurs before the oem change, because we don't want to
942 * update it if chgpasswd failed.
944 * Conditional on lp_unix_password_sync() because we don't want
945 * to touch the unix db unless we have admin permission.
948 if(lp_unix_password_sync() &&
949 !chgpasswd(pdb_get_username(hnd), old_passwd, new_passwd, as_root)) {
950 return NT_STATUS_ACCESS_DENIED;
953 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
954 return NT_STATUS_ACCESS_DENIED;
957 /* Now write it into the file. */
958 ret = pdb_update_sam_account (hnd);
960 if (!ret) {
961 return NT_STATUS_ACCESS_DENIED;
964 return NT_STATUS_OK;