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