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