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