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