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