source: trunk-3.0/source/smbd/chgpasswd.c@ 101

Last change on this file since 101 was 1, checked in by Paul Smedley, 18 years ago

Initial code import

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