source: branches/samba-3.5.x/source3/smbd/chgpasswd.c@ 821

Last change on this file since 821 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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