source: branches/samba-3.0/source/smbd/chgpasswd.c@ 124

Last change on this file since 124 was 124, checked in by Paul Smedley, 17 years ago

Update source to 3.0.28a

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