source: branches/samba-3.3.x/source/smbd/chgpasswd.c@ 318

Last change on this file since 318 was 206, checked in by Herwig Bauernfeind, 16 years ago

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

File size: 31.7 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2004
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
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.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21/* These comments regard the code to change the user's unix password: */
22
23/* fork a child process to exec passwd and write to its
24 * tty to change a users password. This is running as the
25 * user who is attempting to change the password.
26 */
27
28/*
29 * This code was copied/borrowed and stolen from various sources.
30 * The primary source was the poppasswd.c from the authors of POPMail. This software
31 * was included as a client to change passwords using the 'passwd' program
32 * on the remote machine.
33 *
34 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
35 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
36 * and rights to modify, distribute or incorporate this change to the CAP suite or
37 * using it for any other reason are granted, so long as this disclaimer is left intact.
38 */
39
40/*
41 This code was hacked considerably for inclusion in Samba, primarily
42 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
43 of the "password chat" option, which allows the easy runtime
44 specification of the expected sequence of events to change a
45 password.
46 */
47
48#include "includes.h"
49
50extern struct passdb_ops pdb_ops;
51
52static NTSTATUS check_oem_password(const char *user,
53 uchar password_encrypted_with_lm_hash[516],
54 const uchar old_lm_hash_encrypted[16],
55 uchar password_encrypted_with_nt_hash[516],
56 const uchar old_nt_hash_encrypted[16],
57 struct samu **hnd,
58 char **pp_new_passwd);
59
60#if ALLOW_CHANGE_PASSWORD
61
62static int findpty(char **slave)
63{
64 int master = -1;
65 char *line = NULL;
66 SMB_STRUCT_DIR *dirp = NULL;
67 const char *dpname;
68
69 *slave = NULL;
70
71#if defined(HAVE_GRANTPT)
72 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
73 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
74 grantpt(master);
75 unlockpt(master);
76 line = (char *)ptsname(master);
77 if (line) {
78 *slave = SMB_STRDUP(line);
79 }
80
81 if (*slave == NULL) {
82 DEBUG(0,
83 ("findpty: Unable to create master/slave pty pair.\n"));
84 /* Stop fd leak on error. */
85 close(master);
86 return -1;
87 } else {
88 DEBUG(10,
89 ("findpty: Allocated slave pty %s\n", *slave));
90 return (master);
91 }
92 }
93#endif /* HAVE_GRANTPT */
94
95 line = SMB_STRDUP("/dev/ptyXX");
96 if (!line) {
97 return (-1);
98 }
99
100 dirp = sys_opendir("/dev");
101 if (!dirp) {
102 SAFE_FREE(line);
103 return (-1);
104 }
105
106 while ((dpname = readdirname(dirp)) != NULL) {
107 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
108 DEBUG(3,
109 ("pty: try to open %s, line was %s\n", dpname,
110 line));
111 line[8] = dpname[3];
112 line[9] = dpname[4];
113 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
114 DEBUG(3, ("pty: opened %s\n", line));
115 line[5] = 't';
116 *slave = line;
117 sys_closedir(dirp);
118 return (master);
119 }
120 }
121 }
122 sys_closedir(dirp);
123 SAFE_FREE(line);
124 return (-1);
125}
126
127static int dochild(int master, const char *slavedev, const struct passwd *pass,
128 const char *passwordprogram, bool as_root)
129{
130 int slave;
131 struct termios stermios;
132 gid_t gid;
133 uid_t uid;
134 char * const eptrs[1] = { NULL };
135
136 if (pass == NULL)
137 {
138 DEBUG(0,
139 ("dochild: user doesn't exist in the UNIX password database.\n"));
140 return False;
141 }
142
143 gid = pass->pw_gid;
144 uid = pass->pw_uid;
145
146 gain_root_privilege();
147
148 /* Start new session - gets rid of controlling terminal. */
149 if (setsid() < 0)
150 {
151 DEBUG(3,
152 ("Weirdness, couldn't let go of controlling terminal\n"));
153 return (False);
154 }
155
156 /* Open slave pty and acquire as new controlling terminal. */
157 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
158 {
159 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
160 return (False);
161 }
162#if defined(TIOCSCTTY) && !defined(SUNOS5)
163 /*
164 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
165 * see the discussion under
166 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
167 */
168 if (ioctl(slave, TIOCSCTTY, 0) < 0)
169 {
170 DEBUG(3, ("Error in ioctl call for slave pty\n"));
171 /* return(False); */
172 }
173#elif defined(I_PUSH) && defined(I_FIND)
174 if (ioctl(slave, I_FIND, "ptem") == 0) {
175 ioctl(slave, I_PUSH, "ptem");
176 }
177 if (ioctl(slave, I_FIND, "ldterm") == 0) {
178 ioctl(slave, I_PUSH, "ldterm");
179 }
180#endif
181
182 /* Close master. */
183 close(master);
184
185 /* Make slave stdin/out/err of child. */
186
187 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
188 {
189 DEBUG(3, ("Could not re-direct stdin\n"));
190 return (False);
191 }
192 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
193 {
194 DEBUG(3, ("Could not re-direct stdout\n"));
195 return (False);
196 }
197 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
198 {
199 DEBUG(3, ("Could not re-direct stderr\n"));
200 return (False);
201 }
202 if (slave > 2)
203 close(slave);
204
205 /* Set proper terminal attributes - no echo, canonical input processing,
206 no map NL to CR/NL on output. */
207
208 if (tcgetattr(0, &stermios) < 0)
209 {
210 DEBUG(3,
211 ("could not read default terminal attributes on pty\n"));
212 return (False);
213 }
214 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
215 stermios.c_lflag |= ICANON;
216#ifdef ONLCR
217 stermios.c_oflag &= ~(ONLCR);
218#endif
219 if (tcsetattr(0, TCSANOW, &stermios) < 0)
220 {
221 DEBUG(3, ("could not set attributes of pty\n"));
222 return (False);
223 }
224
225 /* make us completely into the right uid */
226 if (!as_root)
227 {
228 become_user_permanently(uid, gid);
229 }
230
231 DEBUG(10,
232 ("Invoking '%s' as password change program.\n",
233 passwordprogram));
234
235 /* execl() password-change application */
236 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
237 {
238 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
239 return (False);
240 }
241 return (True);
242}
243
244static int expect(int master, char *issue, char *expected)
245{
246 char buffer[1024];
247 int attempts, timeout, nread;
248 size_t len;
249 bool match = False;
250
251 for (attempts = 0; attempts < 2; attempts++) {
252 NTSTATUS status;
253 if (!strequal(issue, ".")) {
254 if (lp_passwd_chat_debug())
255 DEBUG(100, ("expect: sending [%s]\n", issue));
256
257 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
258 DEBUG(2,("expect: (short) write returned %d\n",
259 (int)len ));
260 return False;
261 }
262 }
263
264 if (strequal(expected, "."))
265 return True;
266
267 /* Initial timeout. */
268 timeout = lp_passwd_chat_timeout() * 1000;
269 nread = 0;
270 buffer[nread] = 0;
271
272 while (True) {
273 status = read_socket_with_timeout(
274 master, buffer + nread, 1,
275 sizeof(buffer) - nread - 1,
276 timeout, &len);
277
278 if (!NT_STATUS_IS_OK(status)) {
279 break;
280 }
281 nread += len;
282 buffer[nread] = 0;
283
284 {
285 /* Eat leading/trailing whitespace before match. */
286 char *str = SMB_STRDUP(buffer);
287 if (!str) {
288 DEBUG(2,("expect: ENOMEM\n"));
289 return False;
290 }
291 trim_char(str, ' ', ' ');
292
293 if ((match = unix_wild_match(expected, str)) == True) {
294 /* Now data has started to return, lower timeout. */
295 timeout = lp_passwd_chat_timeout() * 100;
296 }
297 SAFE_FREE(str);
298 }
299 }
300
301 if (lp_passwd_chat_debug())
302 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
303 expected, buffer, match ? "yes" : "no" ));
304
305 if (match)
306 break;
307
308 if (!NT_STATUS_IS_OK(status)) {
309 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
310 return False;
311 }
312 }
313
314 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
315 return match;
316}
317
318static void pwd_sub(char *buf)
319{
320 all_string_sub(buf, "\\n", "\n", 0);
321 all_string_sub(buf, "\\r", "\r", 0);
322 all_string_sub(buf, "\\s", " ", 0);
323 all_string_sub(buf, "\\t", "\t", 0);
324}
325
326static int talktochild(int master, const char *seq)
327{
328 TALLOC_CTX *frame = talloc_stackframe();
329 int count = 0;
330 char *issue;
331 char *expected;
332
333 issue = talloc_strdup(frame, ".");
334 if (!issue) {
335 TALLOC_FREE(frame);
336 return false;
337 }
338
339 while (next_token_talloc(frame, &seq, &expected, NULL)) {
340 pwd_sub(expected);
341 count++;
342
343 if (!expect(master, issue, expected)) {
344 DEBUG(3, ("Response %d incorrect\n", count));
345 TALLOC_FREE(frame);
346 return false;
347 }
348
349 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
350 issue = talloc_strdup(frame, ".");
351 if (!issue) {
352 TALLOC_FREE(frame);
353 return false;
354 }
355 }
356 pwd_sub(issue);
357 }
358
359 if (!strequal(issue, ".")) {
360 /* we have one final issue to send */
361 expected = talloc_strdup(frame, ".");
362 if (!expected) {
363 TALLOC_FREE(frame);
364 return false;
365 }
366 if (!expect(master, issue, expected)) {
367 TALLOC_FREE(frame);
368 return False;
369 }
370 }
371 TALLOC_FREE(frame);
372 return (count > 0);
373}
374
375static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
376 char *chatsequence, bool as_root)
377{
378 char *slavedev = NULL;
379 int master;
380 pid_t pid, wpid;
381 int wstat;
382 bool chstat = False;
383
384 if (pass == NULL) {
385 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
386 return False;
387 }
388
389 /* allocate a pseudo-terminal device */
390 if ((master = findpty(&slavedev)) < 0) {
391 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
392 return (False);
393 }
394
395 /*
396 * We need to temporarily stop CatchChild from eating
397 * SIGCLD signals as it also eats the exit status code. JRA.
398 */
399
400 CatchChildLeaveStatus();
401
402 if ((pid = sys_fork()) < 0) {
403 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
404 SAFE_FREE(slavedev);
405 close(master);
406 CatchChild();
407 return (False);
408 }
409
410 /* we now have a pty */
411 if (pid > 0) { /* This is the parent process */
412 /* Don't need this anymore in parent. */
413 SAFE_FREE(slavedev);
414
415 if ((chstat = talktochild(master, chatsequence)) == False) {
416 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
417 kill(pid, SIGKILL); /* be sure to end this process */
418 }
419
420 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
421 if (errno == EINTR) {
422 errno = 0;
423 continue;
424 }
425 break;
426 }
427
428 if (wpid < 0) {
429 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
430 close(master);
431 CatchChild();
432 return (False);
433 }
434
435 /*
436 * Go back to ignoring children.
437 */
438 CatchChild();
439
440 close(master);
441
442 if (pid != wpid) {
443 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
444 return (False);
445 }
446 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
447 DEBUG(3, ("chat_with_program: The process exited with status %d \
448while we were waiting\n", WEXITSTATUS(wstat)));
449 return (False);
450 }
451#if defined(WIFSIGNALLED) && defined(WTERMSIG)
452 else if (WIFSIGNALLED(wstat)) {
453 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
454while we were waiting\n", WTERMSIG(wstat)));
455 return (False);
456 }
457#endif
458 } else {
459 /* CHILD */
460
461 /*
462 * Lose any elevated privileges.
463 */
464 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
465 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
466
467 /* make sure it doesn't freeze */
468 alarm(20);
469
470 if (as_root)
471 become_root();
472
473 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
474 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
475 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
476
477 if (as_root)
478 unbecome_root();
479
480 /*
481 * The child should never return from dochild() ....
482 */
483
484 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
485 exit(1);
486 }
487
488 if (chstat)
489 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
490 (chstat ? "" : "un"), pass->pw_name));
491 return (chstat);
492}
493
494bool chgpasswd(const char *name, const struct passwd *pass,
495 const char *oldpass, const char *newpass, bool as_root)
496{
497 char *passwordprogram = NULL;
498 char *chatsequence = NULL;
499 size_t i;
500 size_t len;
501 TALLOC_CTX *ctx = talloc_tos();
502
503 if (!oldpass) {
504 oldpass = "";
505 }
506
507 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
508
509#ifdef DEBUG_PASSWORD
510 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
511#endif
512
513 /* Take the passed information and test it for minimum criteria */
514
515 /* Password is same as old password */
516 if (strcmp(oldpass, newpass) == 0) {
517 /* don't allow same password */
518 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
519 return (False); /* inform the user */
520 }
521
522 /*
523 * Check the old and new passwords don't contain any control
524 * characters.
525 */
526
527 len = strlen(oldpass);
528 for (i = 0; i < len; i++) {
529 if (iscntrl((int)oldpass[i])) {
530 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
531 return False;
532 }
533 }
534
535 len = strlen(newpass);
536 for (i = 0; i < len; i++) {
537 if (iscntrl((int)newpass[i])) {
538 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
539 return False;
540 }
541 }
542
543#ifdef WITH_PAM
544 if (lp_pam_password_change()) {
545 bool ret;
546#ifdef HAVE_SETLOCALE
547 const char *prevlocale = setlocale(LC_ALL, "C");
548#endif
549
550 if (as_root)
551 become_root();
552
553 if (pass) {
554 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
555 } else {
556 ret = smb_pam_passchange(name, oldpass, newpass);
557 }
558
559 if (as_root)
560 unbecome_root();
561
562#ifdef HAVE_SETLOCALE
563 setlocale(LC_ALL, prevlocale);
564#endif
565
566 return ret;
567 }
568#endif
569
570 /* A non-PAM password change just doen't make sense without a valid local user */
571
572 if (pass == NULL) {
573 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
574 return false;
575 }
576
577 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
578 if (!passwordprogram || !*passwordprogram) {
579 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
580 return false;
581 }
582 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
583 if (!chatsequence || !*chatsequence) {
584 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
585 return false;
586 }
587
588 if (as_root) {
589 /* The password program *must* contain the user name to work. Fail if not. */
590 if (strstr_m(passwordprogram, "%u") == NULL) {
591 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
592the string %%u, and the given string %s does not.\n", passwordprogram ));
593 return false;
594 }
595 }
596
597 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
598 if (!passwordprogram) {
599 return false;
600 }
601
602 /* note that we do NOT substitute the %o and %n in the password program
603 as this would open up a security hole where the user could use
604 a new password containing shell escape characters */
605
606 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
607 if (!chatsequence) {
608 return false;
609 }
610 chatsequence = talloc_all_string_sub(ctx,
611 chatsequence,
612 "%o",
613 oldpass);
614 if (!chatsequence) {
615 return false;
616 }
617 chatsequence = talloc_all_string_sub(ctx,
618 chatsequence,
619 "%n",
620 newpass);
621 return chat_with_program(passwordprogram,
622 pass,
623 chatsequence,
624 as_root);
625}
626
627#else /* ALLOW_CHANGE_PASSWORD */
628
629bool chgpasswd(const char *name, const struct passwd *pass,
630 const char *oldpass, const char *newpass, bool as_root)
631{
632 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
633 return (False);
634}
635#endif /* ALLOW_CHANGE_PASSWORD */
636
637/***********************************************************
638 Code to check the lanman hashed password.
639************************************************************/
640
641bool check_lanman_password(char *user, uchar * pass1,
642 uchar * pass2, struct samu **hnd)
643{
644 uchar unenc_new_pw[16];
645 uchar unenc_old_pw[16];
646 struct samu *sampass = NULL;
647 uint32 acct_ctrl;
648 const uint8 *lanman_pw;
649 bool ret;
650
651 if ( !(sampass = samu_new(NULL)) ) {
652 DEBUG(0, ("samu_new() failed!\n"));
653 return False;
654 }
655
656 become_root();
657 ret = pdb_getsampwnam(sampass, user);
658 unbecome_root();
659
660 if (ret == False) {
661 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
662 TALLOC_FREE(sampass);
663 return False;
664 }
665
666 acct_ctrl = pdb_get_acct_ctrl (sampass);
667 lanman_pw = pdb_get_lanman_passwd (sampass);
668
669 if (acct_ctrl & ACB_DISABLED) {
670 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
671 TALLOC_FREE(sampass);
672 return False;
673 }
674
675 if (lanman_pw == NULL) {
676 if (acct_ctrl & ACB_PWNOTREQ) {
677 /* this saves the pointer for the caller */
678 *hnd = sampass;
679 return True;
680 } else {
681 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
682 TALLOC_FREE(sampass);
683 return False;
684 }
685 }
686
687 /* Get the new lanman hash. */
688 D_P16(lanman_pw, pass2, unenc_new_pw);
689
690 /* Use this to get the old lanman hash. */
691 D_P16(unenc_new_pw, pass1, unenc_old_pw);
692
693 /* Check that the two old passwords match. */
694 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
695 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
696 TALLOC_FREE(sampass);
697 return False;
698 }
699
700 /* this saves the pointer for the caller */
701 *hnd = sampass;
702 return True;
703}
704
705/***********************************************************
706 Code to change the lanman hashed password.
707 It nulls out the NT hashed password as it will
708 no longer be valid.
709 NOTE this function is designed to be called as root. Check the old password
710 is correct before calling. JRA.
711************************************************************/
712
713bool change_lanman_password(struct samu *sampass, uchar *pass2)
714{
715 static uchar null_pw[16];
716 uchar unenc_new_pw[16];
717 bool ret;
718 uint32 acct_ctrl;
719 const uint8 *pwd;
720
721 if (sampass == NULL) {
722 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
723 return False;
724 }
725
726 acct_ctrl = pdb_get_acct_ctrl(sampass);
727 pwd = pdb_get_lanman_passwd(sampass);
728
729 if (acct_ctrl & ACB_DISABLED) {
730 DEBUG(0,("change_lanman_password: account %s disabled.\n",
731 pdb_get_username(sampass)));
732 return False;
733 }
734
735 if (pwd == NULL) {
736 if (acct_ctrl & ACB_PWNOTREQ) {
737 uchar no_pw[14];
738 memset(no_pw, '\0', 14);
739 E_P16(no_pw, null_pw);
740
741 /* Get the new lanman hash. */
742 D_P16(null_pw, pass2, unenc_new_pw);
743 } else {
744 DEBUG(0,("change_lanman_password: no lanman password !\n"));
745 return False;
746 }
747 } else {
748 /* Get the new lanman hash. */
749 D_P16(pwd, pass2, unenc_new_pw);
750 }
751
752 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
753 return False;
754 }
755
756 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
757 return False; /* We lose the NT hash. Sorry. */
758 }
759
760 if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) {
761 TALLOC_FREE(sampass);
762 /* Not quite sure what this one qualifies as, but this will do */
763 return False;
764 }
765
766 /* Now flush the sam_passwd struct to persistent storage */
767 ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
768
769 return ret;
770}
771
772/***********************************************************
773 Code to check and change the OEM hashed password.
774************************************************************/
775
776NTSTATUS pass_oem_change(char *user,
777 uchar password_encrypted_with_lm_hash[516],
778 const uchar old_lm_hash_encrypted[16],
779 uchar password_encrypted_with_nt_hash[516],
780 const uchar old_nt_hash_encrypted[16],
781 uint32 *reject_reason)
782{
783 char *new_passwd = NULL;
784 struct samu *sampass = NULL;
785 NTSTATUS nt_status = check_oem_password(user,
786 password_encrypted_with_lm_hash,
787 old_lm_hash_encrypted,
788 password_encrypted_with_nt_hash,
789 old_nt_hash_encrypted,
790 &sampass,
791 &new_passwd);
792
793 if (!NT_STATUS_IS_OK(nt_status)) {
794 return nt_status;
795 }
796
797 /* We've already checked the old password here.... */
798 become_root();
799 nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
800 unbecome_root();
801
802 memset(new_passwd, 0, strlen(new_passwd));
803
804 TALLOC_FREE(sampass);
805
806 return nt_status;
807}
808
809/***********************************************************
810 Decrypt and verify a user password change.
811
812 The 516 byte long buffers are encrypted with the old NT and
813 old LM passwords, and if the NT passwords are present, both
814 buffers contain a unicode string.
815
816 After decrypting the buffers, check the password is correct by
817 matching the old hashed passwords with the passwords in the passdb.
818
819************************************************************/
820
821static NTSTATUS check_oem_password(const char *user,
822 uchar password_encrypted_with_lm_hash[516],
823 const uchar old_lm_hash_encrypted[16],
824 uchar password_encrypted_with_nt_hash[516],
825 const uchar old_nt_hash_encrypted[16],
826 struct samu **hnd,
827 char **pp_new_passwd)
828{
829 static uchar null_pw[16];
830 static uchar null_ntpw[16];
831 struct samu *sampass = NULL;
832 uint8 *password_encrypted;
833 const uint8 *encryption_key;
834 const uint8 *lanman_pw, *nt_pw;
835 uint32 acct_ctrl;
836 uint32 new_pw_len;
837 uchar new_nt_hash[16];
838 uchar new_lm_hash[16];
839 uchar verifier[16];
840 char no_pw[2];
841 bool ret;
842
843 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
844 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
845
846 *hnd = NULL;
847
848 if ( !(sampass = samu_new( NULL )) ) {
849 return NT_STATUS_NO_MEMORY;
850 }
851
852 become_root();
853 ret = pdb_getsampwnam(sampass, user);
854 unbecome_root();
855
856 if (ret == False) {
857 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
858 TALLOC_FREE(sampass);
859 return NT_STATUS_NO_SUCH_USER;
860 }
861
862 acct_ctrl = pdb_get_acct_ctrl(sampass);
863
864 if (acct_ctrl & ACB_DISABLED) {
865 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
866 TALLOC_FREE(sampass);
867 return NT_STATUS_ACCOUNT_DISABLED;
868 }
869
870 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
871 /* construct a null password (in case one is needed */
872 no_pw[0] = 0;
873 no_pw[1] = 0;
874 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
875 lanman_pw = null_pw;
876 nt_pw = null_pw;
877
878 } else {
879 /* save pointers to passwords so we don't have to keep looking them up */
880 if (lp_lanman_auth()) {
881 lanman_pw = pdb_get_lanman_passwd(sampass);
882 } else {
883 lanman_pw = NULL;
884 }
885 nt_pw = pdb_get_nt_passwd(sampass);
886 }
887
888 if (nt_pw && nt_pass_set) {
889 /* IDEAL Case: passwords are in unicode, and we can
890 * read use the password encrypted with the NT hash
891 */
892 password_encrypted = password_encrypted_with_nt_hash;
893 encryption_key = nt_pw;
894 } else if (lanman_pw && lm_pass_set) {
895 /* password may still be in unicode, but use LM hash version */
896 password_encrypted = password_encrypted_with_lm_hash;
897 encryption_key = lanman_pw;
898 } else if (nt_pass_set) {
899 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
900 user));
901 TALLOC_FREE(sampass);
902 return NT_STATUS_WRONG_PASSWORD;
903 } else if (lm_pass_set) {
904 if (lp_lanman_auth()) {
905 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
906 user));
907 } else {
908 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
909 user));
910 }
911 TALLOC_FREE(sampass);
912 return NT_STATUS_WRONG_PASSWORD;
913 } else {
914 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
915 user));
916 TALLOC_FREE(sampass);
917 return NT_STATUS_WRONG_PASSWORD;
918 }
919
920 /*
921 * Decrypt the password with the key
922 */
923 SamOEMhash( password_encrypted, encryption_key, 516);
924
925 if (!decode_pw_buffer(talloc_tos(),
926 password_encrypted,
927 pp_new_passwd,
928 &new_pw_len,
929 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
930 TALLOC_FREE(sampass);
931 return NT_STATUS_WRONG_PASSWORD;
932 }
933
934 /*
935 * To ensure we got the correct new password, hash it and
936 * use it as a key to test the passed old password.
937 */
938
939 if (nt_pass_set) {
940 /* NT passwords, verify the NT hash. */
941
942 /* Calculate the MD4 hash (NT compatible) of the password */
943 memset(new_nt_hash, '\0', 16);
944 E_md4hash(*pp_new_passwd, new_nt_hash);
945
946 if (nt_pw) {
947 /*
948 * check the NT verifier
949 */
950 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
951 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
952 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
953 TALLOC_FREE(sampass);
954 return NT_STATUS_WRONG_PASSWORD;
955 }
956
957 /* We could check the LM password here, but there is
958 * little point, we already know the password is
959 * correct, and the LM password might not even be
960 * present. */
961
962 /* Further, LM hash generation algorithms
963 * differ with charset, so we could
964 * incorrectly fail a perfectly valid password
965 * change */
966#ifdef DEBUG_PASSWORD
967 DEBUG(100,
968 ("check_oem_password: password %s ok\n", *pp_new_passwd));
969#endif
970 *hnd = sampass;
971 return NT_STATUS_OK;
972 }
973
974 if (lanman_pw) {
975 /*
976 * check the lm verifier
977 */
978 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
979 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
980 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
981 TALLOC_FREE(sampass);
982 return NT_STATUS_WRONG_PASSWORD;
983 }
984#ifdef DEBUG_PASSWORD
985 DEBUG(100,
986 ("check_oem_password: password %s ok\n", *pp_new_passwd));
987#endif
988 *hnd = sampass;
989 return NT_STATUS_OK;
990 }
991 }
992
993 if (lanman_pw && lm_pass_set) {
994
995 E_deshash(*pp_new_passwd, new_lm_hash);
996
997 /*
998 * check the lm verifier
999 */
1000 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
1001 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
1002 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
1003 TALLOC_FREE(sampass);
1004 return NT_STATUS_WRONG_PASSWORD;
1005 }
1006
1007#ifdef DEBUG_PASSWORD
1008 DEBUG(100,
1009 ("check_oem_password: password %s ok\n", *pp_new_passwd));
1010#endif
1011 *hnd = sampass;
1012 return NT_STATUS_OK;
1013 }
1014
1015 /* should not be reached */
1016 TALLOC_FREE(sampass);
1017 return NT_STATUS_WRONG_PASSWORD;
1018}
1019
1020/***********************************************************
1021 This routine takes the given password and checks it against
1022 the password history. Returns True if this password has been
1023 found in the history list.
1024************************************************************/
1025
1026static bool check_passwd_history(struct samu *sampass, const char *plaintext)
1027{
1028 uchar new_nt_p16[NT_HASH_LEN];
1029 uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
1030 const uint8 *nt_pw;
1031 const uint8 *pwhistory;
1032 bool found = False;
1033 int i;
1034 uint32 pwHisLen, curr_pwHisLen;
1035
1036 pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
1037 if (pwHisLen == 0) {
1038 return False;
1039 }
1040
1041 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
1042 if (!pwhistory || curr_pwHisLen == 0) {
1043 return False;
1044 }
1045
1046 /* Only examine the minimum of the current history len and
1047 the stored history len. Avoids race conditions. */
1048 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
1049
1050 nt_pw = pdb_get_nt_passwd(sampass);
1051
1052 E_md4hash(plaintext, new_nt_p16);
1053
1054 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
1055 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
1056 pdb_get_username(sampass) ));
1057 return True;
1058 }
1059
1060 dump_data(100, new_nt_p16, NT_HASH_LEN);
1061 dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
1062
1063 memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
1064 for (i=0; i<pwHisLen; i++) {
1065 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
1066 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
1067 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
1068 PW_HISTORY_SALT_LEN];
1069 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1070 /* Ignore zero valued entries. */
1071 continue;
1072 }
1073 /* Create salted versions of new to compare. */
1074 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1075
1076 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1077 DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1078 pdb_get_username(sampass) ));
1079 found = True;
1080 break;
1081 }
1082 }
1083 return found;
1084}
1085
1086/***********************************************************
1087 Code to change the oem password. Changes both the lanman
1088 and NT hashes. Old_passwd is almost always NULL.
1089 NOTE this function is designed to be called as root. Check the old password
1090 is correct before calling. JRA.
1091************************************************************/
1092
1093NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
1094{
1095 uint32 min_len;
1096 uint32 refuse;
1097 struct passwd *pass = NULL;
1098 const char *username = pdb_get_username(hnd);
1099 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1100
1101 if (samr_reject_reason) {
1102 *samr_reject_reason = Undefined;
1103 }
1104
1105 /* check to see if the secdesc has previously been set to disallow */
1106 if (!pdb_get_pass_can_change(hnd)) {
1107 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1108 if (samr_reject_reason) {
1109 *samr_reject_reason = SAMR_REJECT_OTHER;
1110 }
1111 return NT_STATUS_ACCOUNT_RESTRICTION;
1112 }
1113
1114 /* check to see if it is a Machine account and if the policy
1115 * denies machines to change the password. *
1116 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1117 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1118 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1119 DEBUG(1, ("Machine %s cannot change password now, "
1120 "denied by Refuse Machine Password Change policy\n",
1121 username));
1122 if (samr_reject_reason) {
1123 *samr_reject_reason = SAMR_REJECT_OTHER;
1124 }
1125 return NT_STATUS_ACCOUNT_RESTRICTION;
1126 }
1127 }
1128
1129 /* removed calculation here, becuase passdb now calculates
1130 based on policy. jmcd */
1131 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1132 DEBUG(1, ("user %s cannot change password now, must "
1133 "wait until %s\n", username,
1134 http_timestring(can_change_time)));
1135 if (samr_reject_reason) {
1136 *samr_reject_reason = SAMR_REJECT_OTHER;
1137 }
1138 return NT_STATUS_ACCOUNT_RESTRICTION;
1139 }
1140
1141 if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1142 DEBUG(1, ("user %s cannot change password - password too short\n",
1143 username));
1144 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1145 if (samr_reject_reason) {
1146 *samr_reject_reason = SAMR_REJECT_TOO_SHORT;
1147 }
1148 return NT_STATUS_PASSWORD_RESTRICTION;
1149/* return NT_STATUS_PWD_TOO_SHORT; */
1150 }
1151
1152 if (check_passwd_history(hnd,new_passwd)) {
1153 if (samr_reject_reason) {
1154 *samr_reject_reason = SAMR_REJECT_IN_HISTORY;
1155 }
1156 return NT_STATUS_PASSWORD_RESTRICTION;
1157 }
1158
1159 pass = Get_Pwnam_alloc(talloc_tos(), username);
1160 if (!pass) {
1161 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1162 return NT_STATUS_ACCESS_DENIED;
1163 }
1164
1165 /* Use external script to check password complexity */
1166 if (lp_check_password_script() && *(lp_check_password_script())) {
1167 int check_ret;
1168
1169 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1170 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1171
1172 if (check_ret != 0) {
1173 DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1174 if (samr_reject_reason) {
1175 *samr_reject_reason = SAMR_REJECT_COMPLEXITY;
1176 }
1177 TALLOC_FREE(pass);
1178 return NT_STATUS_PASSWORD_RESTRICTION;
1179 }
1180 }
1181
1182 /*
1183 * If unix password sync was requested, attempt to change
1184 * the /etc/passwd database first. Return failure if this cannot
1185 * be done.
1186 *
1187 * This occurs before the oem change, because we don't want to
1188 * update it if chgpasswd failed.
1189 *
1190 * Conditional on lp_unix_password_sync() because we don't want
1191 * to touch the unix db unless we have admin permission.
1192 */
1193
1194 if(lp_unix_password_sync() &&
1195 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1196 TALLOC_FREE(pass);
1197 return NT_STATUS_ACCESS_DENIED;
1198 }
1199
1200 TALLOC_FREE(pass);
1201
1202 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1203 return NT_STATUS_ACCESS_DENIED;
1204 }
1205
1206 /* Now write it into the file. */
1207 return pdb_update_sam_account (hnd);
1208}
Note: See TracBrowser for help on using the repository browser.