Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into dmapi-integration
[kai/samba.git] / source / smbd / 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
50 extern struct passdb_ops pdb_ops;
51
52 static NTSTATUS check_oem_password(const char *user,
53                                    uchar password_encrypted_with_lm_hash[516],
54                                    const uchar old_lm_hash_encrypted[16],
55                                    uchar password_encrypted_with_nt_hash[516],
56                                    const uchar old_nt_hash_encrypted[16],
57                                    struct samu **hnd,
58                                    char **pp_new_passwd);
59
60 #if ALLOW_CHANGE_PASSWORD
61
62 static int findpty(char **slave)
63 {
64         int master = -1;
65         char *line = NULL;
66         SMB_STRUCT_DIR *dirp = NULL;
67         const char *dpname;
68
69         *slave = NULL;
70
71 #if defined(HAVE_GRANTPT)
72         /* Try to open /dev/ptmx. If that fails, fall through to old method. */
73         if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
74                 grantpt(master);
75                 unlockpt(master);
76                 line = (char *)ptsname(master);
77                 if (line) {
78                         *slave = SMB_STRDUP(line);
79                 }
80
81                 if (*slave == NULL) {
82                         DEBUG(0,
83                               ("findpty: Unable to create master/slave pty pair.\n"));
84                         /* Stop fd leak on error. */
85                         close(master);
86                         return -1;
87                 } else {
88                         DEBUG(10,
89                               ("findpty: Allocated slave pty %s\n", *slave));
90                         return (master);
91                 }
92         }
93 #endif /* HAVE_GRANTPT */
94
95         line = SMB_STRDUP("/dev/ptyXX");
96         if (!line) {
97                 return (-1);
98         }
99
100         dirp = sys_opendir("/dev");
101         if (!dirp) {
102                 SAFE_FREE(line);
103                 return (-1);
104         }
105
106         while ((dpname = readdirname(dirp)) != NULL) {
107                 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
108                         DEBUG(3,
109                               ("pty: try to open %s, line was %s\n", dpname,
110                                line));
111                         line[8] = dpname[3];
112                         line[9] = dpname[4];
113                         if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
114                                 DEBUG(3, ("pty: opened %s\n", line));
115                                 line[5] = 't';
116                                 *slave = line;
117                                 sys_closedir(dirp);
118                                 return (master);
119                         }
120                 }
121         }
122         sys_closedir(dirp);
123         SAFE_FREE(line);
124         return (-1);
125 }
126
127 static int dochild(int master, const char *slavedev, const struct passwd *pass,
128                    const char *passwordprogram, bool as_root)
129 {
130         int slave;
131         struct termios stermios;
132         gid_t gid;
133         uid_t uid;
134         char * const eptrs[1] = { NULL };
135
136         if (pass == NULL)
137         {
138                 DEBUG(0,
139                       ("dochild: user doesn't exist in the UNIX password database.\n"));
140                 return False;
141         }
142
143         gid = pass->pw_gid;
144         uid = pass->pw_uid;
145
146         gain_root_privilege();
147
148         /* Start new session - gets rid of controlling terminal. */
149         if (setsid() < 0)
150         {
151                 DEBUG(3,
152                       ("Weirdness, couldn't let go of controlling terminal\n"));
153                 return (False);
154         }
155
156         /* Open slave pty and acquire as new controlling terminal. */
157         if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
158         {
159                 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
160                 return (False);
161         }
162 #if defined(TIOCSCTTY)
163         if (ioctl(slave, TIOCSCTTY, 0) < 0)
164         {
165                 DEBUG(3, ("Error in ioctl call for slave pty\n"));
166                 /* return(False); */
167         }
168 #elif defined(I_PUSH) && defined(I_FIND)
169         if (ioctl(slave, I_FIND, "ptem") == 0) {
170                 ioctl(slave, I_PUSH, "ptem");
171         }
172         if (ioctl(slave, I_FIND, "ldterm") == 0) {
173                 ioctl(slave, I_PUSH, "ldterm");
174         }
175 #endif
176
177         /* Close master. */
178         close(master);
179
180         /* Make slave stdin/out/err of child. */
181
182         if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
183         {
184                 DEBUG(3, ("Could not re-direct stdin\n"));
185                 return (False);
186         }
187         if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
188         {
189                 DEBUG(3, ("Could not re-direct stdout\n"));
190                 return (False);
191         }
192         if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
193         {
194                 DEBUG(3, ("Could not re-direct stderr\n"));
195                 return (False);
196         }
197         if (slave > 2)
198                 close(slave);
199
200         /* Set proper terminal attributes - no echo, canonical input processing,
201            no map NL to CR/NL on output. */
202
203         if (tcgetattr(0, &stermios) < 0)
204         {
205                 DEBUG(3,
206                       ("could not read default terminal attributes on pty\n"));
207                 return (False);
208         }
209         stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
210         stermios.c_lflag |= ICANON;
211 #ifdef ONLCR
212         stermios.c_oflag &= ~(ONLCR);
213 #endif
214         if (tcsetattr(0, TCSANOW, &stermios) < 0)
215         {
216                 DEBUG(3, ("could not set attributes of pty\n"));
217                 return (False);
218         }
219
220         /* make us completely into the right uid */
221         if (!as_root)
222         {
223                 become_user_permanently(uid, gid);
224         }
225
226         DEBUG(10,
227               ("Invoking '%s' as password change program.\n",
228                passwordprogram));
229
230         /* execl() password-change application */
231         if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
232         {
233                 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
234                 return (False);
235         }
236         return (True);
237 }
238
239 static int expect(int master, char *issue, char *expected)
240 {
241         char buffer[1024];
242         int attempts, timeout, nread;
243         size_t len;
244         bool match = False;
245
246         for (attempts = 0; attempts < 2; attempts++) {
247                 if (!strequal(issue, ".")) {
248                         if (lp_passwd_chat_debug())
249                                 DEBUG(100, ("expect: sending [%s]\n", issue));
250
251                         if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
252                                 DEBUG(2,("expect: (short) write returned %d\n",
253                                          (int)len ));
254                                 return False;
255                         }
256                 }
257
258                 if (strequal(expected, "."))
259                         return True;
260
261                 /* Initial timeout. */
262                 timeout = lp_passwd_chat_timeout() * 1000;
263                 nread = 0;
264                 buffer[nread] = 0;
265
266                 while (True) {
267                         NTSTATUS status;
268                         status = read_socket_with_timeout(
269                                 master, buffer + nread, 1,
270                                 sizeof(buffer) - nread - 1,
271                                 timeout, &len);
272
273                         if (!NT_STATUS_IS_OK(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 (len < 0) {
304                         DEBUG(2, ("expect: %s\n", strerror(errno)));
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 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, oldpass, newpass);
550                 } else {
551                         ret = smb_pam_passchange(name, oldpass, newpass);
552                 }
553
554                 if (as_root)
555                         unbecome_root();
556
557 #ifdef HAVE_SETLOCALE
558                 setlocale(LC_ALL, prevlocale);
559 #endif
560
561                 return ret;
562         }
563 #endif
564
565         /* A non-PAM password change just doen't make sense without a valid local user */
566
567         if (pass == NULL) {
568                 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
569                 return false;
570         }
571
572         passwordprogram = talloc_strdup(ctx, lp_passwd_program());
573         if (!passwordprogram || !*passwordprogram) {
574                 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
575                 return false;
576         }
577         chatsequence = talloc_strdup(ctx, lp_passwd_chat());
578         if (!chatsequence || !*chatsequence) {
579                 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
580                 return false;
581         }
582
583         if (as_root) {
584                 /* The password program *must* contain the user name to work. Fail if not. */
585                 if (strstr_m(passwordprogram, "%u") == NULL) {
586                         DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
587 the string %%u, and the given string %s does not.\n", passwordprogram ));
588                         return false;
589                 }
590         }
591
592         passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
593         if (!passwordprogram) {
594                 return false;
595         }
596
597         /* note that we do NOT substitute the %o and %n in the password program
598            as this would open up a security hole where the user could use
599            a new password containing shell escape characters */
600
601         chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
602         if (!chatsequence) {
603                 return false;
604         }
605         chatsequence = talloc_all_string_sub(ctx,
606                                         chatsequence,
607                                         "%o",
608                                         oldpass);
609         if (!chatsequence) {
610                 return false;
611         }
612         chatsequence = talloc_all_string_sub(ctx,
613                                         chatsequence,
614                                         "%n",
615                                         newpass);
616         return chat_with_program(passwordprogram,
617                                 pass,
618                                 chatsequence,
619                                 as_root);
620 }
621
622 #else /* ALLOW_CHANGE_PASSWORD */
623
624 bool chgpasswd(const char *name, const struct passwd *pass, 
625                const char *oldpass, const char *newpass, bool as_root)
626 {
627         DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
628         return (False);
629 }
630 #endif /* ALLOW_CHANGE_PASSWORD */
631
632 /***********************************************************
633  Code to check the lanman hashed password.
634 ************************************************************/
635
636 bool check_lanman_password(char *user, uchar * pass1,
637                            uchar * pass2, struct samu **hnd)
638 {
639         uchar unenc_new_pw[16];
640         uchar unenc_old_pw[16];
641         struct samu *sampass = NULL;
642         uint32 acct_ctrl;
643         const uint8 *lanman_pw;
644         bool ret;
645
646         if ( !(sampass = samu_new(NULL)) ) {
647                 DEBUG(0, ("samu_new() failed!\n"));
648                 return False;
649         }
650         
651         become_root();
652         ret = pdb_getsampwnam(sampass, user);
653         unbecome_root();
654
655         if (ret == False) {
656                 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
657                 TALLOC_FREE(sampass);
658                 return False;
659         }
660         
661         acct_ctrl = pdb_get_acct_ctrl     (sampass);
662         lanman_pw = pdb_get_lanman_passwd (sampass);
663
664         if (acct_ctrl & ACB_DISABLED) {
665                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
666                 TALLOC_FREE(sampass);
667                 return False;
668         }
669
670         if (lanman_pw == NULL) {
671                 if (acct_ctrl & ACB_PWNOTREQ) {
672                         /* this saves the pointer for the caller */
673                         *hnd = sampass;
674                         return True;
675                 } else {
676                         DEBUG(0, ("check_lanman_password: no lanman password !\n"));
677                         TALLOC_FREE(sampass);
678                         return False;
679                 }
680         }
681
682         /* Get the new lanman hash. */
683         D_P16(lanman_pw, pass2, unenc_new_pw);
684
685         /* Use this to get the old lanman hash. */
686         D_P16(unenc_new_pw, pass1, unenc_old_pw);
687
688         /* Check that the two old passwords match. */
689         if (memcmp(lanman_pw, unenc_old_pw, 16)) {
690                 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
691                 TALLOC_FREE(sampass);
692                 return False;
693         }
694
695         /* this saves the pointer for the caller */
696         *hnd = sampass;
697         return True;
698 }
699
700 /***********************************************************
701  Code to change the lanman hashed password.
702  It nulls out the NT hashed password as it will
703  no longer be valid.
704  NOTE this function is designed to be called as root. Check the old password
705  is correct before calling. JRA.
706 ************************************************************/
707
708 bool change_lanman_password(struct samu *sampass, uchar *pass2)
709 {
710         static uchar null_pw[16];
711         uchar unenc_new_pw[16];
712         bool ret;
713         uint32 acct_ctrl;
714         const uint8 *pwd;
715
716         if (sampass == NULL) {
717                 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
718                 return False;
719         }
720         
721         acct_ctrl = pdb_get_acct_ctrl(sampass);
722         pwd = pdb_get_lanman_passwd(sampass);
723
724         if (acct_ctrl & ACB_DISABLED) {
725                 DEBUG(0,("change_lanman_password: account %s disabled.\n",
726                        pdb_get_username(sampass)));
727                 return False;
728         }
729
730         if (pwd == NULL) { 
731                 if (acct_ctrl & ACB_PWNOTREQ) {
732                         uchar no_pw[14];
733                         memset(no_pw, '\0', 14);
734                         E_P16(no_pw, null_pw);
735
736                         /* Get the new lanman hash. */
737                         D_P16(null_pw, pass2, unenc_new_pw);
738                 } else {
739                         DEBUG(0,("change_lanman_password: no lanman password !\n"));
740                         return False;
741                 }
742         } else {
743                 /* Get the new lanman hash. */
744                 D_P16(pwd, pass2, unenc_new_pw);
745         }
746
747         if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
748                 return False;
749         }
750
751         if (!pdb_set_nt_passwd    (sampass, NULL, PDB_CHANGED)) {
752                 return False;   /* We lose the NT hash. Sorry. */
753         }
754
755         if (!pdb_set_pass_last_set_time  (sampass, time(NULL), PDB_CHANGED)) {
756                 TALLOC_FREE(sampass);
757                 /* Not quite sure what this one qualifies as, but this will do */
758                 return False;
759         }
760
761         /* Now flush the sam_passwd struct to persistent storage */
762         ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
763
764         return ret;
765 }
766
767 /***********************************************************
768  Code to check and change the OEM hashed password.
769 ************************************************************/
770
771 NTSTATUS pass_oem_change(char *user,
772                          uchar password_encrypted_with_lm_hash[516],
773                          const uchar old_lm_hash_encrypted[16],
774                          uchar password_encrypted_with_nt_hash[516],
775                          const uchar old_nt_hash_encrypted[16],
776                          uint32 *reject_reason)
777 {
778         char *new_passwd = NULL;
779         struct samu *sampass = NULL;
780         NTSTATUS nt_status = check_oem_password(user,
781                                                 password_encrypted_with_lm_hash,
782                                                 old_lm_hash_encrypted,
783                                                 password_encrypted_with_nt_hash,
784                                                 old_nt_hash_encrypted,
785                                                 &sampass,
786                                                 &new_passwd);
787
788         if (!NT_STATUS_IS_OK(nt_status)) {
789                 return nt_status;
790         }
791
792         /* We've already checked the old password here.... */
793         become_root();
794         nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
795         unbecome_root();
796
797         memset(new_passwd, 0, strlen(new_passwd));
798
799         TALLOC_FREE(sampass);
800
801         return nt_status;
802 }
803
804 /***********************************************************
805  Decrypt and verify a user password change.
806
807  The 516 byte long buffers are encrypted with the old NT and
808  old LM passwords, and if the NT passwords are present, both
809  buffers contain a unicode string.
810
811  After decrypting the buffers, check the password is correct by
812  matching the old hashed passwords with the passwords in the passdb.
813
814 ************************************************************/
815
816 static NTSTATUS check_oem_password(const char *user,
817                                    uchar password_encrypted_with_lm_hash[516],
818                                    const uchar old_lm_hash_encrypted[16],
819                                    uchar password_encrypted_with_nt_hash[516],
820                                    const uchar old_nt_hash_encrypted[16],
821                                    struct samu **hnd,
822                                    char **pp_new_passwd)
823 {
824         static uchar null_pw[16];
825         static uchar null_ntpw[16];
826         struct samu *sampass = NULL;
827         uint8 *password_encrypted;
828         const uint8 *encryption_key;
829         const uint8 *lanman_pw, *nt_pw;
830         uint32 acct_ctrl;
831         uint32 new_pw_len;
832         uchar new_nt_hash[16];
833         uchar new_lm_hash[16];
834         uchar verifier[16];
835         char no_pw[2];
836         bool ret;
837
838         bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
839         bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
840
841         *hnd = NULL;
842
843         if ( !(sampass = samu_new( NULL )) ) {
844                 return NT_STATUS_NO_MEMORY;
845         }
846
847         become_root();
848         ret = pdb_getsampwnam(sampass, user);
849         unbecome_root();
850
851         if (ret == False) {
852                 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
853                 TALLOC_FREE(sampass);
854                 return NT_STATUS_NO_SUCH_USER;
855         }
856
857         acct_ctrl = pdb_get_acct_ctrl(sampass);
858
859         if (acct_ctrl & ACB_DISABLED) {
860                 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
861                 TALLOC_FREE(sampass);
862                 return NT_STATUS_ACCOUNT_DISABLED;
863         }
864
865         if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
866                 /* construct a null password (in case one is needed */
867                 no_pw[0] = 0;
868                 no_pw[1] = 0;
869                 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
870                 lanman_pw = null_pw;
871                 nt_pw = null_pw;
872
873         } else {
874                 /* save pointers to passwords so we don't have to keep looking them up */
875                 if (lp_lanman_auth()) {
876                         lanman_pw = pdb_get_lanman_passwd(sampass);
877                 } else {
878                         lanman_pw = NULL;
879                 }
880                 nt_pw = pdb_get_nt_passwd(sampass);
881         }
882
883         if (nt_pw && nt_pass_set) {
884                 /* IDEAL Case: passwords are in unicode, and we can
885                  * read use the password encrypted with the NT hash
886                  */
887                 password_encrypted = password_encrypted_with_nt_hash;
888                 encryption_key = nt_pw;
889         } else if (lanman_pw && lm_pass_set) {
890                 /* password may still be in unicode, but use LM hash version */
891                 password_encrypted = password_encrypted_with_lm_hash;
892                 encryption_key = lanman_pw;
893         } else if (nt_pass_set) {
894                 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n", 
895                           user));
896                 TALLOC_FREE(sampass);
897                 return NT_STATUS_WRONG_PASSWORD;
898         } else if (lm_pass_set) {
899                 if (lp_lanman_auth()) {
900                         DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n", 
901                                   user));
902                 } else {
903                         DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n", 
904                                   user));
905                 }
906                 TALLOC_FREE(sampass);
907                 return NT_STATUS_WRONG_PASSWORD;
908         } else {
909                 DEBUG(1, ("password change requested for user %s, but no password supplied!\n", 
910                           user));
911                 TALLOC_FREE(sampass);
912                 return NT_STATUS_WRONG_PASSWORD;
913         }
914
915         /*
916          * Decrypt the password with the key
917          */
918         SamOEMhash( password_encrypted, encryption_key, 516);
919
920         if (!decode_pw_buffer(talloc_tos(),
921                                 password_encrypted,
922                                 pp_new_passwd,
923                                 &new_pw_len,
924                                 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
925                 TALLOC_FREE(sampass);
926                 return NT_STATUS_WRONG_PASSWORD;
927         }
928
929         /*
930          * To ensure we got the correct new password, hash it and
931          * use it as a key to test the passed old password.
932          */
933
934         if (nt_pass_set) {
935                 /* NT passwords, verify the NT hash. */
936
937                 /* Calculate the MD4 hash (NT compatible) of the password */
938                 memset(new_nt_hash, '\0', 16);
939                 E_md4hash(*pp_new_passwd, new_nt_hash);
940
941                 if (nt_pw) {
942                         /*
943                          * check the NT verifier
944                          */
945                         E_old_pw_hash(new_nt_hash, nt_pw, verifier);
946                         if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
947                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
948                                 TALLOC_FREE(sampass);
949                                 return NT_STATUS_WRONG_PASSWORD;
950                         }
951
952                         /* We could check the LM password here, but there is
953                          * little point, we already know the password is
954                          * correct, and the LM password might not even be
955                          * present. */
956
957                         /* Further, LM hash generation algorithms
958                          * differ with charset, so we could
959                          * incorrectly fail a perfectly valid password
960                          * change */
961 #ifdef DEBUG_PASSWORD
962                         DEBUG(100,
963                               ("check_oem_password: password %s ok\n", *pp_new_passwd));
964 #endif
965                         *hnd = sampass;
966                         return NT_STATUS_OK;
967                 }
968
969                 if (lanman_pw) {
970                         /*
971                          * check the lm verifier
972                          */
973                         E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
974                         if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
975                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
976                                 TALLOC_FREE(sampass);
977                                 return NT_STATUS_WRONG_PASSWORD;
978                         }
979 #ifdef DEBUG_PASSWORD
980                         DEBUG(100,
981                               ("check_oem_password: password %s ok\n", *pp_new_passwd));
982 #endif
983                         *hnd = sampass;
984                         return NT_STATUS_OK;
985                 }
986         }
987
988         if (lanman_pw && lm_pass_set) {
989
990                 E_deshash(*pp_new_passwd, new_lm_hash);
991
992                 /*
993                  * check the lm verifier
994                  */
995                 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
996                 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
997                         DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
998                         TALLOC_FREE(sampass);
999                         return NT_STATUS_WRONG_PASSWORD;
1000                 }
1001
1002 #ifdef DEBUG_PASSWORD
1003                 DEBUG(100,
1004                       ("check_oem_password: password %s ok\n", *pp_new_passwd));
1005 #endif
1006                 *hnd = sampass;
1007                 return NT_STATUS_OK;
1008         }
1009
1010         /* should not be reached */
1011         TALLOC_FREE(sampass);
1012         return NT_STATUS_WRONG_PASSWORD;
1013 }
1014
1015 /***********************************************************
1016  This routine takes the given password and checks it against
1017  the password history. Returns True if this password has been
1018  found in the history list.
1019 ************************************************************/
1020
1021 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
1022 {
1023         uchar new_nt_p16[NT_HASH_LEN];
1024         uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
1025         const uint8 *nt_pw;
1026         const uint8 *pwhistory;
1027         bool found = False;
1028         int i;
1029         uint32 pwHisLen, curr_pwHisLen;
1030
1031         pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
1032         if (pwHisLen == 0) {
1033                 return False;
1034         }
1035
1036         pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
1037         if (!pwhistory || curr_pwHisLen == 0) {
1038                 return False;
1039         }
1040
1041         /* Only examine the minimum of the current history len and
1042            the stored history len. Avoids race conditions. */
1043         pwHisLen = MIN(pwHisLen,curr_pwHisLen);
1044
1045         nt_pw = pdb_get_nt_passwd(sampass);
1046
1047         E_md4hash(plaintext, new_nt_p16);
1048
1049         if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
1050                 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
1051                         pdb_get_username(sampass) ));
1052                 return True;
1053         }
1054
1055         dump_data(100, new_nt_p16, NT_HASH_LEN);
1056         dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
1057
1058         memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
1059         for (i=0; i<pwHisLen; i++) {
1060                 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
1061                 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
1062                 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
1063                                                         PW_HISTORY_SALT_LEN];
1064                 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1065                         /* Ignore zero valued entries. */
1066                         continue;
1067                 }
1068                 /* Create salted versions of new to compare. */
1069                 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1070
1071                 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1072                         DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1073                                 pdb_get_username(sampass) ));
1074                         found = True;
1075                         break;
1076                 }
1077         }
1078         return found;
1079 }
1080
1081 /***********************************************************
1082  Code to change the oem password. Changes both the lanman
1083  and NT hashes.  Old_passwd is almost always NULL.
1084  NOTE this function is designed to be called as root. Check the old password
1085  is correct before calling. JRA.
1086 ************************************************************/
1087
1088 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
1089 {
1090         uint32 min_len;
1091         uint32 refuse;
1092         struct passwd *pass = NULL;
1093         const char *username = pdb_get_username(hnd);
1094         time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1095
1096         if (samr_reject_reason) {
1097                 *samr_reject_reason = Undefined;
1098         }
1099
1100         /* check to see if the secdesc has previously been set to disallow */
1101         if (!pdb_get_pass_can_change(hnd)) {
1102                 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1103                 if (samr_reject_reason) {
1104                         *samr_reject_reason = SAMR_REJECT_OTHER;
1105                 }
1106                 return NT_STATUS_ACCOUNT_RESTRICTION;
1107         }
1108
1109         /* check to see if it is a Machine account and if the policy
1110          * denies machines to change the password. *
1111          * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1112         if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1113                 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1114                         DEBUG(1, ("Machine %s cannot change password now, "
1115                                   "denied by Refuse Machine Password Change policy\n",
1116                                   username));
1117                         if (samr_reject_reason) {
1118                                 *samr_reject_reason = SAMR_REJECT_OTHER;
1119                         }
1120                         return NT_STATUS_ACCOUNT_RESTRICTION;
1121                 }
1122         }
1123
1124         /* removed calculation here, becuase passdb now calculates
1125            based on policy.  jmcd */
1126         if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1127                 DEBUG(1, ("user %s cannot change password now, must "
1128                           "wait until %s\n", username,
1129                           http_timestring(can_change_time)));
1130                 if (samr_reject_reason) {
1131                         *samr_reject_reason = SAMR_REJECT_OTHER;
1132                 }
1133                 return NT_STATUS_ACCOUNT_RESTRICTION;
1134         }
1135
1136         if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1137                 DEBUG(1, ("user %s cannot change password - password too short\n", 
1138                           username));
1139                 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1140                 if (samr_reject_reason) {
1141                         *samr_reject_reason = SAMR_REJECT_TOO_SHORT;
1142                 }
1143                 return NT_STATUS_PASSWORD_RESTRICTION;
1144 /*              return NT_STATUS_PWD_TOO_SHORT; */
1145         }
1146
1147         if (check_passwd_history(hnd,new_passwd)) {
1148                 if (samr_reject_reason) {
1149                         *samr_reject_reason = SAMR_REJECT_IN_HISTORY;
1150                 }
1151                 return NT_STATUS_PASSWORD_RESTRICTION;
1152         }
1153
1154         pass = Get_Pwnam_alloc(talloc_tos(), username);
1155         if (!pass) {
1156                 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1157                 return NT_STATUS_ACCESS_DENIED;
1158         }
1159
1160         /* Use external script to check password complexity */
1161         if (lp_check_password_script() && *(lp_check_password_script())) {
1162                 int check_ret;
1163
1164                 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1165                 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1166
1167                 if (check_ret != 0) {
1168                         DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1169                         if (samr_reject_reason) {
1170                                 *samr_reject_reason = SAMR_REJECT_COMPLEXITY;
1171                         }
1172                         TALLOC_FREE(pass);
1173                         return NT_STATUS_PASSWORD_RESTRICTION;
1174                 }
1175         }
1176
1177         /*
1178          * If unix password sync was requested, attempt to change
1179          * the /etc/passwd database first. Return failure if this cannot
1180          * be done.
1181          *
1182          * This occurs before the oem change, because we don't want to
1183          * update it if chgpasswd failed.
1184          *
1185          * Conditional on lp_unix_password_sync() because we don't want
1186          * to touch the unix db unless we have admin permission.
1187          */
1188         
1189         if(lp_unix_password_sync() &&
1190                 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1191                 TALLOC_FREE(pass);
1192                 return NT_STATUS_ACCESS_DENIED;
1193         }
1194
1195         TALLOC_FREE(pass);
1196
1197         if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1198                 return NT_STATUS_ACCESS_DENIED;
1199         }
1200
1201         /* Now write it into the file. */
1202         return pdb_update_sam_account (hnd);
1203 }