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