Removed 'extern int DEBUGLEVEL' as it is now in the smb.h header.
[tprouty/samba.git] / source / smbd / chgpasswd.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Samba utility functions
5    Copyright (C) Andrew Tridgell 1992-1998
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 /* fork a child process to exec passwd and write to its
23  * tty to change a users password. This is running as the
24  * user who is attempting to change the password.
25  */
26
27 /* 
28  * This code was copied/borrowed and stolen from various sources.
29  * The primary source was the poppasswd.c from the authors of POPMail. This software
30  * was included as a client to change passwords using the 'passwd' program
31  * on the remote machine.
32  *
33  * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
34  * is defined in the compiler directives located in the Makefile.
35  *
36  * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
37  * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
38  * and rights to modify, distribute or incorporate this change to the CAP suite or
39  * using it for any other reason are granted, so long as this disclaimer is left intact.
40  */
41
42 /*
43    This code was hacked considerably for inclusion in Samba, primarily
44    by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
45    of the "password chat" option, which allows the easy runtime
46    specification of the expected sequence of events to change a
47    password.
48    */
49
50 #include "includes.h"
51
52 extern struct passdb_ops pdb_ops;
53
54 static BOOL check_oem_password(char *user,
55                                uchar * lmdata, uchar * lmhash,
56                                uchar * ntdata, uchar * nthash,
57                                SAM_ACCOUNT **hnd, char *new_passwd,
58                                int new_passwd_size);
59
60 #if ALLOW_CHANGE_PASSWORD
61
62 static int findpty(char **slave)
63 {
64         int master;
65         static fstring line;
66         DIR *dirp;
67         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 = 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                                 closedir(dirp);
113                                 return (master);
114                         }
115                 }
116         }
117         closedir(dirp);
118         return (-1);
119 }
120
121 static int dochild(int master, char *slavedev, char *name,
122                    char *passwordprogram, BOOL as_root)
123 {
124         int slave;
125         struct termios stermios;
126         struct passwd *pass = Get_Pwnam(name, True);
127         gid_t gid;
128         uid_t uid;
129
130         if (pass == NULL)
131         {
132                 DEBUG(0,
133                       ("dochild: user name %s doesn't exist in the UNIX password database.\n",
134                        name));
135                 return False;
136         }
137
138         gid = pass->pw_gid;
139         uid = pass->pw_uid;
140
141         gain_root_privilege();
142
143         /* Start new session - gets rid of controlling terminal. */
144         if (setsid() < 0)
145         {
146                 DEBUG(3,
147                       ("Weirdness, couldn't let go of controlling terminal\n"));
148                 return (False);
149         }
150
151         /* Open slave pty and acquire as new controlling terminal. */
152         if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
153         {
154                 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
155                 return (False);
156         }
157 #ifdef I_PUSH
158         ioctl(slave, I_PUSH, "ptem");
159         ioctl(slave, I_PUSH, "ldterm");
160 #elif defined(TIOCSCTTY)
161         if (ioctl(slave, TIOCSCTTY, 0) < 0)
162         {
163                 DEBUG(3, ("Error in ioctl call for slave pty\n"));
164                 /* return(False); */
165         }
166 #endif
167
168         /* Close master. */
169         close(master);
170
171         /* Make slave stdin/out/err of child. */
172
173         if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
174         {
175                 DEBUG(3, ("Could not re-direct stdin\n"));
176                 return (False);
177         }
178         if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
179         {
180                 DEBUG(3, ("Could not re-direct stdout\n"));
181                 return (False);
182         }
183         if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
184         {
185                 DEBUG(3, ("Could not re-direct stderr\n"));
186                 return (False);
187         }
188         if (slave > 2)
189                 close(slave);
190
191         /* Set proper terminal attributes - no echo, canonical input processing,
192            no map NL to CR/NL on output. */
193
194         if (tcgetattr(0, &stermios) < 0)
195         {
196                 DEBUG(3,
197                       ("could not read default terminal attributes on pty\n"));
198                 return (False);
199         }
200         stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
201         stermios.c_lflag |= ICANON;
202         stermios.c_oflag &= ~(ONLCR);
203         if (tcsetattr(0, TCSANOW, &stermios) < 0)
204         {
205                 DEBUG(3, ("could not set attributes of pty\n"));
206                 return (False);
207         }
208
209         /* make us completely into the right uid */
210         if (!as_root)
211         {
212                 become_user_permanently(uid, gid);
213         }
214
215         DEBUG(10,
216               ("Invoking '%s' as password change program.\n",
217                passwordprogram));
218
219         /* execl() password-change application */
220         if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
221         {
222                 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
223                 return (False);
224         }
225         return (True);
226 }
227
228 static int expect(int master, char *issue, char *expected)
229 {
230         pstring buffer;
231         int attempts, timeout, nread, len;
232         BOOL match = False;
233
234         for (attempts = 0; attempts < 2; attempts++)
235         {
236                 if (!strequal(issue, "."))
237                 {
238                         if (lp_passwd_chat_debug())
239                                 DEBUG(100, ("expect: sending [%s]\n", issue));
240
241                         write(master, issue, strlen(issue));
242                 }
243
244                 if (strequal(expected, "."))
245                         return True;
246
247                 timeout = 2000;
248                 nread = 0;
249                 buffer[nread] = 0;
250
251                 while ((len = read_with_timeout(master, buffer + nread, 1,
252                                                 sizeof(buffer) - nread - 1,
253                                                 timeout)) > 0)
254                 {
255                         nread += len;
256                         buffer[nread] = 0;
257
258                         if ((match = (wild_match(expected, buffer) == 0)))
259                                 timeout = 200;
260                 }
261
262                 if (lp_passwd_chat_debug())
263                         DEBUG(100, ("expect: expected [%s] received [%s]\n",
264                                     expected, buffer));
265
266                 if (match)
267                         break;
268
269                 if (len < 0)
270                 {
271                         DEBUG(2, ("expect: %s\n", strerror(errno)));
272                         return False;
273                 }
274         }
275
276         return match;
277 }
278
279 static void pwd_sub(char *buf)
280 {
281         all_string_sub(buf, "\\n", "\n", 0);
282         all_string_sub(buf, "\\r", "\r", 0);
283         all_string_sub(buf, "\\s", " ", 0);
284         all_string_sub(buf, "\\t", "\t", 0);
285 }
286
287 static int talktochild(int master, char *seq)
288 {
289         int count = 0;
290         fstring issue, expected;
291
292         fstrcpy(issue, ".");
293
294         while (next_token(&seq, expected, NULL, sizeof(expected)))
295         {
296                 pwd_sub(expected);
297                 count++;
298
299                 if (!expect(master, issue, expected))
300                 {
301                         DEBUG(3, ("Response %d incorrect\n", count));
302                         return False;
303                 }
304
305                 if (!next_token(&seq, issue, NULL, sizeof(issue)))
306                         fstrcpy(issue, ".");
307
308                 pwd_sub(issue);
309         }
310
311         return (count > 0);
312 }
313
314 static BOOL chat_with_program(char *passwordprogram, char *name,
315                               char *chatsequence, BOOL as_root)
316 {
317         char *slavedev;
318         int master;
319         pid_t pid, wpid;
320         int wstat;
321         BOOL chstat = False;
322
323         /* allocate a pseudo-terminal device */
324         if ((master = findpty(&slavedev)) < 0)
325         {
326                 DEBUG(3,
327                       ("Cannot Allocate pty for password change: %s\n",
328                        name));
329                 return (False);
330         }
331
332         /*
333          * We need to temporarily stop CatchChild from eating
334          * SIGCLD signals as it also eats the exit status code. JRA.
335          */
336
337         CatchChildLeaveStatus();
338
339         if ((pid = sys_fork()) < 0)
340         {
341                 DEBUG(3,
342                       ("Cannot fork() child for password change: %s\n",
343                        name));
344                 close(master);
345                 CatchChild();
346                 return (False);
347         }
348
349         /* we now have a pty */
350         if (pid > 0)
351         {                       /* This is the parent process */
352                 if ((chstat = talktochild(master, chatsequence)) == False)
353                 {
354                         DEBUG(3,
355                               ("Child failed to change password: %s\n",
356                                name));
357                         kill(pid, SIGKILL);     /* be sure to end this process */
358                 }
359
360                 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
361                 {
362                         if (errno == EINTR)
363                         {
364                                 errno = 0;
365                                 continue;
366                         }
367                         break;
368                 }
369
370                 if (wpid < 0)
371                 {
372                         DEBUG(3, ("The process is no longer waiting!\n\n"));
373                         close(master);
374                         CatchChild();
375                         return (False);
376                 }
377
378                 /*
379                  * Go back to ignoring children.
380                  */
381                 CatchChild();
382
383                 close(master);
384
385                 if (pid != wpid)
386                 {
387                         DEBUG(3,
388                               ("We were waiting for the wrong process ID\n"));
389                         return (False);
390                 }
391                 if (WIFEXITED(wstat) == 0)
392                 {
393                         DEBUG(3,
394                               ("The process exited while we were waiting\n"));
395                         return (False);
396                 }
397                 if (WEXITSTATUS(wstat) != 0)
398                 {
399                         DEBUG(3,
400                               ("The status of the process exiting was %d\n",
401                                wstat));
402                         return (False);
403                 }
404
405         }
406         else
407         {
408                 /* CHILD */
409
410                 /*
411                  * Lose any oplock capabilities.
412                  */
413                 oplock_set_capability(False, False);
414
415                 /* make sure it doesn't freeze */
416                 alarm(20);
417
418                 if (as_root)
419                         become_root();
420
421                 DEBUG(3,
422                       ("Dochild for user %s (uid=%d,gid=%d)\n", name,
423                        (int)getuid(), (int)getgid()));
424                 chstat =
425                         dochild(master, slavedev, name, passwordprogram,
426                                 as_root);
427
428                 if (as_root)
429                         unbecome_root();
430
431                 /*
432                  * The child should never return from dochild() ....
433                  */
434
435                 DEBUG(0,
436                       ("chat_with_program: Error: dochild() returned %d\n",
437                        chstat));
438                 exit(1);
439         }
440
441         if (chstat)
442                 DEBUG(3,
443                       ("Password change %ssuccessful for user %s\n",
444                        (chstat ? "" : "un"), name));
445         return (chstat);
446 }
447
448
449 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
450 {
451         pstring passwordprogram;
452         pstring chatsequence;
453         size_t i;
454         size_t len;
455
456         strlower(name);
457         DEBUG(3, ("Password change for user: %s\n", name));
458
459 #if DEBUG_PASSWORD
460         DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
461 #endif
462
463         /* Take the passed information and test it for minimum criteria */
464         /* Minimum password length */
465         if (strlen(newpass) < lp_min_passwd_length()) {
466                 /* too short, must be at least MINPASSWDLENGTH */
467                 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
468                        name, lp_min_passwd_length()));
469                 return (False); /* inform the user */
470         }
471
472         /* Password is same as old password */
473         if (strcmp(oldpass, newpass) == 0) {
474                 /* don't allow same password */
475                 DEBUG(2, ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
476                 return (False); /* inform the user */
477         }
478
479         /* 
480          * Check the old and new passwords don't contain any control
481          * characters.
482          */
483
484         len = strlen(oldpass);
485         for (i = 0; i < len; i++) {
486                 if (iscntrl((int)oldpass[i])) {
487                         DEBUG(0,
488                               ("chat_with_program: oldpass contains control characters (disallowed).\n"));
489                         return False;
490                 }
491         }
492
493         len = strlen(newpass);
494         for (i = 0; i < len; i++) {
495                 if (iscntrl((int)newpass[i])) {
496                         DEBUG(0,
497                               ("chat_with_program: newpass contains control characters (disallowed).\n"));
498                         return False;
499                 }
500         }
501
502 #ifdef WITH_PAM
503         if (lp_pam_password_change()) {
504                 BOOL ret;
505
506                 if (as_root)
507                         become_root();
508
509                 ret = smb_pam_passchange(name, oldpass, newpass);
510
511                 if (as_root)
512                         unbecome_root();
513
514                 return ret;
515         }
516 #endif
517
518         pstrcpy(passwordprogram, lp_passwd_program());
519         pstrcpy(chatsequence, lp_passwd_chat());
520
521         if (!*chatsequence) {
522                 DEBUG(2, ("Null chat sequence - no password changing\n"));
523                 return (False);
524         }
525
526         if (!*passwordprogram) {
527                 DEBUG(2, ("Null password program - no password changing\n"));
528                 return (False);
529         }
530
531         pstring_sub(passwordprogram, "%u", name);
532         /* note that we do NOT substitute the %o and %n in the password program
533            as this would open up a security hole where the user could use
534            a new password containing shell escape characters */
535
536         pstring_sub(chatsequence, "%u", name);
537         all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
538         all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
539         return (chat_with_program
540                 (passwordprogram, name, chatsequence, as_root));
541 }
542
543 #else /* ALLOW_CHANGE_PASSWORD */
544
545 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
546 {
547         DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
548         return (False);
549 }
550 #endif /* ALLOW_CHANGE_PASSWORD */
551
552 /***********************************************************
553  Code to check the lanman hashed password.
554 ************************************************************/
555
556 BOOL check_lanman_password(char *user, uchar * pass1,
557                            uchar * pass2, SAM_ACCOUNT **hnd)
558 {
559         uchar unenc_new_pw[16];
560         uchar unenc_old_pw[16];
561         SAM_ACCOUNT *sampass = NULL;
562         uint16 acct_ctrl;
563         uint8 *lanman_pw;
564         BOOL ret;
565         
566         become_root();
567         ret = pdb_getsampwnam(sampass, user);
568         unbecome_root();
569
570         if (ret == False) {
571                 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
572                 pdb_free_sam(&sampass);
573                 return False;
574         }
575         
576         acct_ctrl = pdb_get_acct_ctrl     (sampass);
577         lanman_pw = pdb_get_lanman_passwd (sampass);
578
579         if (acct_ctrl & ACB_DISABLED) {
580                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
581                 pdb_free_sam(&sampass);
582                 return False;
583         }
584
585         if (lanman_pw == NULL) {
586                 if (acct_ctrl & ACB_PWNOTREQ) {
587                         /* this saves the pointer for the caller */
588                         *hnd = sampass;
589                         return True;
590                 } else {
591                         DEBUG(0, ("check_lanman_password: no lanman password !\n"));
592                         pdb_free_sam(&sampass);
593                         return False;
594                 }
595         }
596
597         /* Get the new lanman hash. */
598         D_P16(lanman_pw, pass2, unenc_new_pw);
599
600         /* Use this to get the old lanman hash. */
601         D_P16(unenc_new_pw, pass1, unenc_old_pw);
602
603         /* Check that the two old passwords match. */
604         if (memcmp(lanman_pw, unenc_old_pw, 16)) {
605                 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
606                 pdb_free_sam(&sampass);
607                 return False;
608         }
609
610         /* this saves the pointer for the caller */
611         *hnd = sampass;
612         return True;
613 }
614
615 /***********************************************************
616  Code to change the lanman hashed password.
617  It nulls out the NT hashed password as it will
618  no longer be valid.
619 ************************************************************/
620
621 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1,
622                             uchar * pass2)
623 {
624         static uchar null_pw[16];
625         uchar unenc_new_pw[16];
626         BOOL ret;
627         uint16 acct_ctrl;
628         uint8 *pwd;
629
630         if (sampass == NULL) {
631                 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
632                 return False;
633         }
634         
635         acct_ctrl = pdb_get_acct_ctrl(sampass);
636         pwd = pdb_get_lanman_passwd(sampass);
637
638         if (acct_ctrl & ACB_DISABLED) {
639                 DEBUG(0,("change_lanman_password: account %s disabled.\n",
640                        pdb_get_username(sampass)));
641                 return False;
642         }
643
644         if (pwd == NULL) { 
645                 if (acct_ctrl & ACB_PWNOTREQ) {
646                         uchar no_pw[14];
647                         memset(no_pw, '\0', 14);
648                         E_P16(no_pw, null_pw);
649
650                         /* Get the new lanman hash. */
651                         D_P16(null_pw, pass2, unenc_new_pw);
652                 } else {
653                         DEBUG(0,("change_lanman_password: no lanman password !\n"));
654                         return False;
655                 }
656         } else {
657                 /* Get the new lanman hash. */
658                 D_P16(pwd, pass2, unenc_new_pw);
659         }
660
661         if (!pdb_set_lanman_passwd(sampass, unenc_new_pw)) {
662                 return False;
663         }
664
665         if (!pdb_set_nt_passwd    (sampass, NULL)) {
666                 return False;   /* We lose the NT hash. Sorry. */
667         }
668
669         /* Now flush the sam_passwd struct to persistent storage */
670         become_root();
671         ret = pdb_update_sam_account (sampass, False);
672         unbecome_root();
673
674         return ret;
675 }
676
677 /***********************************************************
678  Code to check and change the OEM hashed password.
679 ************************************************************/
680 BOOL pass_oem_change(char *user,
681                      uchar * lmdata, uchar * lmhash,
682                      uchar * ntdata, uchar * nthash)
683 {
684         fstring new_passwd;
685         SAM_ACCOUNT *sampass = NULL;
686         BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
687                                       &sampass, new_passwd, sizeof(new_passwd));
688
689         /* 
690          * At this point we have the new case-sensitive plaintext
691          * password in the fstring new_passwd. If we wanted to synchronise
692          * with UNIX passwords we would call a UNIX password changing 
693          * function here. However it would have to be done as root
694          * as the plaintext of the old users password is not 
695          * available. JRA.
696          */
697
698         if ((ret) && lp_unix_password_sync())
699                 ret = chgpasswd(user, "", new_passwd, True);
700
701         if (ret)
702                 ret = change_oem_password(sampass, new_passwd);
703
704         memset(new_passwd, 0, sizeof(new_passwd));
705
706         pdb_free_sam(&sampass);
707
708         return ret;
709 }
710
711 /***********************************************************
712  Code to check the OEM hashed password.
713
714  this function ignores the 516 byte nt OEM hashed password
715  but does use the lm OEM password to check the nt hashed-hash.
716
717 ************************************************************/
718 static BOOL check_oem_password(char *user,
719                                uchar * lmdata, uchar * lmhash,
720                                uchar * ntdata, uchar * nthash,
721                                SAM_ACCOUNT **hnd, char *new_passwd,
722                                int new_passwd_size)
723 {
724         static uchar null_pw[16];
725         static uchar null_ntpw[16];
726         SAM_ACCOUNT *sampass = NULL;
727         uint8 *lanman_pw, *nt_pw;
728         uint16 acct_ctrl;
729         int new_pw_len;
730         uchar new_ntp16[16];
731         uchar unenc_old_ntpw[16];
732         uchar new_p16[16];
733         uchar unenc_old_pw[16];
734         char no_pw[2];
735         BOOL ret;
736
737         BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
738
739         pdb_init_sam(&sampass);
740
741         become_root();
742         ret = pdb_getsampwnam(sampass, user);
743         unbecome_root();
744
745         if (ret == False) {
746                 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
747                 return False;
748         }
749
750         *hnd = sampass;
751         
752         acct_ctrl = pdb_get_acct_ctrl(sampass);
753         
754         if (acct_ctrl & ACB_DISABLED) {
755                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
756                 return False;
757         }
758
759         /* construct a null password (in case one is needed */
760         no_pw[0] = 0;
761         no_pw[1] = 0;
762         nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
763
764         /* save pointers to passwords so we don't have to keep looking them up */
765         lanman_pw = pdb_get_lanman_passwd(sampass);
766         nt_pw     = pdb_get_nt_passwd    (sampass);
767
768         /* check for null passwords */
769         if (lanman_pw == NULL) {
770                 if (!(acct_ctrl & ACB_PWNOTREQ)) {
771                         DEBUG(0,("check_oem_password: no lanman password !\n"));
772                         return False;
773                 }
774         }
775         
776         if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
777                 if (!(acct_ctrl & ACB_PWNOTREQ)) {
778                         DEBUG(0,("check_oem_password: no ntlm password !\n"));
779                         return False;
780                 }
781         }
782         
783         /* 
784          * Call the hash function to get the new password.
785          */
786         SamOEMhash((uchar *) lmdata, (uchar *)lanman_pw, 516);
787
788         /* 
789          * The length of the new password is in the last 4 bytes of
790          * the data buffer.
791          */
792
793         new_pw_len = IVAL(lmdata, 512);
794         if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
795                 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
796                 return False;
797         }
798
799         if (nt_pass_set) {
800                 /*
801                  * nt passwords are in unicode
802                  */
803                 pull_ucs2(NULL, new_passwd, 
804                           (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
805                           new_passwd_size, new_pw_len, 0);
806         } else {
807                 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
808                 new_passwd[new_pw_len] = 0;
809         }
810
811         /*
812          * To ensure we got the correct new password, hash it and
813          * use it as a key to test the passed old password.
814          */
815
816         nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
817
818         if (!nt_pass_set)
819         {
820                 /*
821                  * Now use new_p16 as the key to see if the old
822                  * password matches.
823                  */
824                 D_P16(new_p16, lmhash, unenc_old_pw);
825
826                 if (memcmp(lanman_pw, unenc_old_pw, 16))
827                 {
828                         DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
829                         return False;
830                 }
831
832 #ifdef DEBUG_PASSWORD
833                 DEBUG(100,
834                       ("check_oem_password: password %s ok\n", new_passwd));
835 #endif
836                 return True;
837         }
838
839         /*
840          * Now use new_p16 as the key to see if the old
841          * password matches.
842          */
843         D_P16(new_ntp16, lmhash, unenc_old_pw);
844         D_P16(new_ntp16, nthash, unenc_old_ntpw);
845
846         if (memcmp(lanman_pw, unenc_old_pw, 16))
847         {
848                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
849                 return False;
850         }
851
852         if (memcmp(nt_pw, unenc_old_ntpw, 16))
853         {
854                 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
855                 return False;
856         }
857 #ifdef DEBUG_PASSWORD
858         DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
859 #endif
860         return True;
861 }
862
863 /***********************************************************
864  Code to change the oem password. Changes both the lanman
865  and NT hashes.
866 ************************************************************/
867
868 BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd)
869 {
870         BOOL ret;
871
872         if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
873                 return False;
874         }
875
876         /* Now write it into the file. */
877         become_root();
878         ret = pdb_update_sam_account (hnd, False);
879         unbecome_root();
880
881         return ret;
882 }
883
884 /***********************************************************
885  Code to check a plaintext password against smbpasswd entries.
886 ***********************************************************/
887
888 BOOL check_plaintext_password(char *user, char *old_passwd,
889                               int old_passwd_size, SAM_ACCOUNT **hnd)
890 {
891         SAM_ACCOUNT  *sampass = NULL;
892         uchar old_pw[16], old_ntpw[16];
893         BOOL ret;
894
895         pdb_init_sam(&sampass);
896
897         become_root();
898         ret = pdb_getsampwnam(sampass, user);
899         unbecome_root();
900
901         *hnd = sampass;
902
903         if (ret == False)
904         {
905                 DEBUG(0,("check_plaintext_password: getsmbpwnam returned NULL\n"));
906                 return False;
907         }
908
909         if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED)
910         {
911                 DEBUG(0,("check_plaintext_password: account %s disabled.\n", user));
912                 return (False);
913         }
914
915         nt_lm_owf_gen(old_passwd, old_ntpw, old_pw);
916
917 #ifdef DEBUG_PASSWORD
918         DEBUG(100, ("check_plaintext_password: nt_passwd \n"));
919         dump_data(100, pdb_get_nt_passwd(sampass), 16);
920         DEBUG(100, ("check_plaintext_password: old_ntpw \n"));
921         dump_data(100, old_ntpw, 16);
922         DEBUG(100, ("check_plaintext_password: lanman_passwd \n"));
923         dump_data(100, pdb_get_lanman_passwd(sampass), 16);
924         DEBUG(100, ("check_plaintext_password: old_pw\n"));
925         dump_data(100, old_pw, 16);
926 #endif
927
928         if (memcmp(pdb_get_nt_passwd(sampass), old_ntpw, 16)
929             && memcmp(pdb_get_lanman_passwd(sampass), old_pw, 16))
930                 return (False);
931         else
932                 return (True);
933 }