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