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