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