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