This is another *BIG* change...
[kai/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);
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         const char *unix_user;
736         SAM_ACCOUNT *sampass = NULL;
737         BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
738                                       &sampass, new_passwd, sizeof(new_passwd));
739
740         /* 
741          * At this point we have the new case-sensitive plaintext
742          * password in the fstring new_passwd. If we wanted to synchronise
743          * with UNIX passwords we would call a UNIX password changing 
744          * function here. However it would have to be done as root
745          * as the plaintext of the old users password is not 
746          * available. JRA.
747          */
748
749         unix_user = pdb_get_username(sampass);
750
751         if ((ret) && (unix_user) && (*unix_user) && lp_unix_password_sync())
752                 ret = chgpasswd(unix_user, "", new_passwd, True);
753
754         if (ret)
755                 ret = change_oem_password(sampass, new_passwd);
756
757         memset(new_passwd, 0, sizeof(new_passwd));
758
759         pdb_free_sam(&sampass);
760
761         return ret;
762 }
763
764 /***********************************************************
765  Code to check the OEM hashed password.
766
767  this function ignores the 516 byte nt OEM hashed password
768  but does use the lm OEM password to check the nt hashed-hash.
769
770 ************************************************************/
771 static BOOL check_oem_password(const char *user,
772                                uchar * lmdata, const uchar * lmhash,
773                                const uchar * ntdata, const uchar * nthash,
774                                SAM_ACCOUNT **hnd, char *new_passwd,
775                                int new_passwd_size)
776 {
777         static uchar null_pw[16];
778         static uchar null_ntpw[16];
779         SAM_ACCOUNT *sampass = NULL;
780         const uint8 *lanman_pw, *nt_pw;
781         uint16 acct_ctrl;
782         int new_pw_len;
783         uchar new_ntp16[16];
784         uchar unenc_old_ntpw[16];
785         uchar new_p16[16];
786         uchar unenc_old_pw[16];
787         char no_pw[2];
788         BOOL ret;
789
790         BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
791
792         pdb_init_sam(&sampass);
793
794         become_root();
795         ret = pdb_getsampwnam(sampass, user);
796         unbecome_root();
797
798         if (ret == False) {
799                 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
800                 return False;
801         }
802
803         *hnd = sampass;
804         
805         acct_ctrl = pdb_get_acct_ctrl(sampass);
806         
807         if (acct_ctrl & ACB_DISABLED) {
808                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
809                 return False;
810         }
811
812         /* construct a null password (in case one is needed */
813         no_pw[0] = 0;
814         no_pw[1] = 0;
815         nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
816
817         /* save pointers to passwords so we don't have to keep looking them up */
818         lanman_pw = pdb_get_lanman_passwd(sampass);
819         nt_pw     = pdb_get_nt_passwd    (sampass);
820
821         /* check for null passwords */
822         if (lanman_pw == NULL) {
823                 if (!(acct_ctrl & ACB_PWNOTREQ)) {
824                         DEBUG(0,("check_oem_password: no lanman password !\n"));
825                         return False;
826                 }
827         }
828         
829         if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
830                 if (!(acct_ctrl & ACB_PWNOTREQ)) {
831                         DEBUG(0,("check_oem_password: no ntlm password !\n"));
832                         return False;
833                 }
834         }
835         
836         /* 
837          * Call the hash function to get the new password.
838          */
839         SamOEMhash( lmdata, lanman_pw, 516);
840
841         /* 
842          * The length of the new password is in the last 4 bytes of
843          * the data buffer.
844          */
845
846         new_pw_len = IVAL(lmdata, 512);
847         if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
848                 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
849                 return False;
850         }
851
852         if (nt_pass_set) {
853                 /*
854                  * nt passwords are in unicode
855                  */
856                 pull_ucs2(NULL, new_passwd, 
857                           (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
858                           new_passwd_size, new_pw_len, 0);
859         } else {
860                 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
861                 new_passwd[new_pw_len] = 0;
862         }
863
864         /*
865          * To ensure we got the correct new password, hash it and
866          * use it as a key to test the passed old password.
867          */
868
869         nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
870
871         if (!nt_pass_set)
872         {
873                 /*
874                  * Now use new_p16 as the key to see if the old
875                  * password matches.
876                  */
877                 D_P16(new_p16, lmhash, unenc_old_pw);
878
879                 if (memcmp(lanman_pw, unenc_old_pw, 16))
880                 {
881                         DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
882                         return False;
883                 }
884
885 #ifdef DEBUG_PASSWORD
886                 DEBUG(100,
887                       ("check_oem_password: password %s ok\n", new_passwd));
888 #endif
889                 return True;
890         }
891
892         /*
893          * Now use new_p16 as the key to see if the old
894          * password matches.
895          */
896         D_P16(new_ntp16, lmhash, unenc_old_pw);
897         D_P16(new_ntp16, nthash, unenc_old_ntpw);
898
899         if (memcmp(lanman_pw, unenc_old_pw, 16))
900         {
901                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
902                 return False;
903         }
904
905         if (memcmp(nt_pw, unenc_old_ntpw, 16))
906         {
907                 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
908                 return False;
909         }
910 #ifdef DEBUG_PASSWORD
911         DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
912 #endif
913         return True;
914 }
915
916 /***********************************************************
917  Code to change the oem password. Changes both the lanman
918  and NT hashes.
919 ************************************************************/
920
921 BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd)
922 {
923         BOOL ret;
924
925         if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
926                 return False;
927         }
928
929         /* Now write it into the file. */
930         become_root();
931         ret = pdb_update_sam_account (hnd);
932         unbecome_root();
933
934         return ret;
935 }
936
937
938