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