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