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

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

Update source to 3.0.29

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