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