Note when we're setting change time, not write time, and send
[tprouty/samba.git] / source / 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-2004
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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 /* These comments regard the code to change the user's unix password: */
22
23 /* fork a child process to exec passwd and write to its
24  * tty to change a users password. This is running as the
25  * user who is attempting to change the password.
26  */
27
28 /* 
29  * This code was copied/borrowed and stolen from various sources.
30  * The primary source was the poppasswd.c from the authors of POPMail. This software
31  * was included as a client to change passwords using the 'passwd' program
32  * on the remote machine.
33  *
34  * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
35  * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
36  * and rights to modify, distribute or incorporate this change to the CAP suite or
37  * using it for any other reason are granted, so long as this disclaimer is left intact.
38  */
39
40 /*
41    This code was hacked considerably for inclusion in Samba, primarily
42    by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
43    of the "password chat" option, which allows the easy runtime
44    specification of the expected sequence of events to change a
45    password.
46    */
47
48 #include "includes.h"
49
50 extern struct passdb_ops pdb_ops;
51
52 static NTSTATUS check_oem_password(const char *user,
53                                    uchar password_encrypted_with_lm_hash[516], 
54                                    const uchar old_lm_hash_encrypted[16],
55                                    uchar password_encrypted_with_nt_hash[516], 
56                                    const uchar old_nt_hash_encrypted[16],
57                                    struct samu **hnd, char *new_passwd,
58                                    int new_passwd_size);
59
60 #if ALLOW_CHANGE_PASSWORD
61
62 static int findpty(char **slave)
63 {
64         int master;
65         static fstring line;
66         SMB_STRUCT_DIR *dirp;
67         const char *dpname;
68
69 #if defined(HAVE_GRANTPT)
70         /* Try to open /dev/ptmx. If that fails, fall through to old method. */
71         if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
72         {
73                 grantpt(master);
74                 unlockpt(master);
75                 *slave = (char *)ptsname(master);
76                 if (*slave == NULL)
77                 {
78                         DEBUG(0,
79                               ("findpty: Unable to create master/slave pty pair.\n"));
80                         /* Stop fd leak on error. */
81                         close(master);
82                         return -1;
83                 }
84                 else
85                 {
86                         DEBUG(10,
87                               ("findpty: Allocated slave pty %s\n", *slave));
88                         return (master);
89                 }
90         }
91 #endif /* HAVE_GRANTPT */
92
93         fstrcpy(line, "/dev/ptyXX");
94
95         dirp = sys_opendir("/dev");
96         if (!dirp)
97                 return (-1);
98         while ((dpname = readdirname(dirp)) != NULL)
99         {
100                 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
101                 {
102                         DEBUG(3,
103                               ("pty: try to open %s, line was %s\n", dpname,
104                                line));
105                         line[8] = dpname[3];
106                         line[9] = dpname[4];
107                         if ((master = sys_open(line, O_RDWR, 0)) >= 0)
108                         {
109                                 DEBUG(3, ("pty: opened %s\n", line));
110                                 line[5] = 't';
111                                 *slave = line;
112                                 sys_closedir(dirp);
113                                 return (master);
114                         }
115                 }
116         }
117         sys_closedir(dirp);
118         return (-1);
119 }
120
121 static int dochild(int master, const char *slavedev, const struct passwd *pass,
122                    const char *passwordprogram, bool as_root)
123 {
124         int slave;
125         struct termios stermios;
126         gid_t gid;
127         uid_t uid;
128
129         if (pass == NULL)
130         {
131                 DEBUG(0,
132                       ("dochild: user doesn't exist in the UNIX password database.\n"));
133                 return False;
134         }
135
136         gid = pass->pw_gid;
137         uid = pass->pw_uid;
138
139         gain_root_privilege();
140
141         /* Start new session - gets rid of controlling terminal. */
142         if (setsid() < 0)
143         {
144                 DEBUG(3,
145                       ("Weirdness, couldn't let go of controlling terminal\n"));
146                 return (False);
147         }
148
149         /* Open slave pty and acquire as new controlling terminal. */
150         if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
151         {
152                 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
153                 return (False);
154         }
155 #if defined(I_PUSH) && defined(I_FIND)
156         if (ioctl(slave, I_FIND, "ptem") == 0) {
157                 ioctl(slave, I_PUSH, "ptem");
158         }
159         if (ioctl(slave, I_FIND, "ldterm") == 0) {
160                 ioctl(slave, I_PUSH, "ldterm");
161         }
162 #elif defined(TIOCSCTTY)
163         if (ioctl(slave, TIOCSCTTY, 0) < 0)
164         {
165                 DEBUG(3, ("Error in ioctl call for slave pty\n"));
166                 /* return(False); */
167         }
168 #endif
169
170         /* Close master. */
171         close(master);
172
173         /* Make slave stdin/out/err of child. */
174
175         if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
176         {
177                 DEBUG(3, ("Could not re-direct stdin\n"));
178                 return (False);
179         }
180         if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
181         {
182                 DEBUG(3, ("Could not re-direct stdout\n"));
183                 return (False);
184         }
185         if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
186         {
187                 DEBUG(3, ("Could not re-direct stderr\n"));
188                 return (False);
189         }
190         if (slave > 2)
191                 close(slave);
192
193         /* Set proper terminal attributes - no echo, canonical input processing,
194            no map NL to CR/NL on output. */
195
196         if (tcgetattr(0, &stermios) < 0)
197         {
198                 DEBUG(3,
199                       ("could not read default terminal attributes on pty\n"));
200                 return (False);
201         }
202         stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
203         stermios.c_lflag |= ICANON;
204 #ifdef ONLCR
205         stermios.c_oflag &= ~(ONLCR);
206 #endif
207         if (tcsetattr(0, TCSANOW, &stermios) < 0)
208         {
209                 DEBUG(3, ("could not set attributes of pty\n"));
210                 return (False);
211         }
212
213         /* make us completely into the right uid */
214         if (!as_root)
215         {
216                 become_user_permanently(uid, gid);
217         }
218
219         DEBUG(10,
220               ("Invoking '%s' as password change program.\n",
221                passwordprogram));
222
223         /* execl() password-change application */
224         if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
225         {
226                 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
227                 return (False);
228         }
229         return (True);
230 }
231
232 static int expect(int master, char *issue, char *expected)
233 {
234         pstring buffer;
235         int attempts, timeout, nread, len;
236         bool match = False;
237
238         for (attempts = 0; attempts < 2; attempts++) {
239                 if (!strequal(issue, ".")) {
240                         if (lp_passwd_chat_debug())
241                                 DEBUG(100, ("expect: sending [%s]\n", issue));
242
243                         if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
244                                 DEBUG(2,("expect: (short) write returned %d\n", len ));
245                                 return False;
246                         }
247                 }
248
249                 if (strequal(expected, "."))
250                         return True;
251
252                 /* Initial timeout. */
253                 timeout = lp_passwd_chat_timeout() * 1000;
254                 nread = 0;
255                 buffer[nread] = 0;
256
257                 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
258                                                        sizeof(buffer) - nread - 1,
259                                                        timeout)) > 0) {
260                         nread += len;
261                         buffer[nread] = 0;
262
263                         {
264                                 /* Eat leading/trailing whitespace before match. */
265                                 pstring str;
266                                 pstrcpy( str, buffer);
267                                 trim_char( str, ' ', ' ');
268
269                                 if ((match = unix_wild_match(expected, str)) == True) {
270                                         /* Now data has started to return, lower timeout. */
271                                         timeout = lp_passwd_chat_timeout() * 100;
272                                 }
273                         }
274                 }
275
276                 if (lp_passwd_chat_debug())
277                         DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
278                                     expected, buffer, match ? "yes" : "no" ));
279
280                 if (match)
281                         break;
282
283                 if (len < 0) {
284                         DEBUG(2, ("expect: %s\n", strerror(errno)));
285                         return False;
286                 }
287         }
288
289         DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
290         return match;
291 }
292
293 static void pwd_sub(char *buf)
294 {
295         all_string_sub(buf, "\\n", "\n", 0);
296         all_string_sub(buf, "\\r", "\r", 0);
297         all_string_sub(buf, "\\s", " ", 0);
298         all_string_sub(buf, "\\t", "\t", 0);
299 }
300
301 static int talktochild(int master, const char *seq)
302 {
303         int count = 0;
304         fstring issue, expected;
305
306         fstrcpy(issue, ".");
307
308         while (next_token(&seq, expected, NULL, sizeof(expected)))
309         {
310                 pwd_sub(expected);
311                 count++;
312
313                 if (!expect(master, issue, expected))
314                 {
315                         DEBUG(3, ("Response %d incorrect\n", count));
316                         return False;
317                 }
318
319                 if (!next_token(&seq, issue, NULL, sizeof(issue)))
320                         fstrcpy(issue, ".");
321
322                 pwd_sub(issue);
323         }
324         if (!strequal(issue, ".")) {
325                 /* we have one final issue to send */
326                 fstrcpy(expected, ".");
327                 if (!expect(master, issue, expected))
328                         return False;
329         }
330
331         return (count > 0);
332 }
333
334 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
335                               char *chatsequence, bool as_root)
336 {
337         char *slavedev;
338         int master;
339         pid_t pid, wpid;
340         int wstat;
341         bool chstat = False;
342
343         if (pass == NULL) {
344                 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
345                 return False;
346         }
347
348         /* allocate a pseudo-terminal device */
349         if ((master = findpty(&slavedev)) < 0) {
350                 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
351                 return (False);
352         }
353
354         /*
355          * We need to temporarily stop CatchChild from eating
356          * SIGCLD signals as it also eats the exit status code. JRA.
357          */
358
359         CatchChildLeaveStatus();
360
361         if ((pid = sys_fork()) < 0) {
362                 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
363                 close(master);
364                 CatchChild();
365                 return (False);
366         }
367
368         /* we now have a pty */
369         if (pid > 0) {                  /* This is the parent process */
370                 if ((chstat = talktochild(master, chatsequence)) == False) {
371                         DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
372                         kill(pid, SIGKILL);     /* be sure to end this process */
373                 }
374
375                 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
376                         if (errno == EINTR) {
377                                 errno = 0;
378                                 continue;
379                         }
380                         break;
381                 }
382
383                 if (wpid < 0) {
384                         DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
385                         close(master);
386                         CatchChild();
387                         return (False);
388                 }
389
390                 /*
391                  * Go back to ignoring children.
392                  */
393                 CatchChild();
394
395                 close(master);
396
397                 if (pid != wpid) {
398                         DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
399                         return (False);
400                 }
401                 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
402                         DEBUG(3, ("chat_with_program: The process exited with status %d \
403 while we were waiting\n", WEXITSTATUS(wstat)));
404                         return (False);
405                 }
406 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
407                 else if (WIFSIGNALLED(wstat)) {
408                         DEBUG(3, ("chat_with_program: The process was killed by signal %d \
409 while we were waiting\n", WTERMSIG(wstat)));
410                         return (False);
411                 }
412 #endif
413         } else {
414                 /* CHILD */
415
416                 /*
417                  * Lose any elevated privileges.
418                  */
419                 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
420                 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
421
422                 /* make sure it doesn't freeze */
423                 alarm(20);
424
425                 if (as_root)
426                         become_root();
427
428                 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
429                        (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
430                 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
431
432                 if (as_root)
433                         unbecome_root();
434
435                 /*
436                  * The child should never return from dochild() ....
437                  */
438
439                 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
440                 exit(1);
441         }
442
443         if (chstat)
444                 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
445                        (chstat ? "" : "un"), pass->pw_name));
446         return (chstat);
447 }
448
449 bool chgpasswd(const char *name, const struct passwd *pass, 
450                const char *oldpass, const char *newpass, bool as_root)
451 {
452         pstring passwordprogram;
453         pstring chatsequence;
454         size_t i;
455         size_t len;
456
457         if (!oldpass) {
458                 oldpass = "";
459         }
460
461         DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
462
463 #ifdef DEBUG_PASSWORD
464         DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
465 #endif
466
467         /* Take the passed information and test it for minimum criteria */
468
469         /* Password is same as old password */
470         if (strcmp(oldpass, newpass) == 0) {
471                 /* don't allow same password */
472                 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name));      /* log the attempt */
473                 return (False); /* inform the user */
474         }
475
476         /* 
477          * Check the old and new passwords don't contain any control
478          * characters.
479          */
480
481         len = strlen(oldpass);
482         for (i = 0; i < len; i++) {
483                 if (iscntrl((int)oldpass[i])) {
484                         DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
485                         return False;
486                 }
487         }
488
489         len = strlen(newpass);
490         for (i = 0; i < len; i++) {
491                 if (iscntrl((int)newpass[i])) {
492                         DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
493                         return False;
494                 }
495         }
496         
497 #ifdef WITH_PAM
498         if (lp_pam_password_change()) {
499                 bool ret;
500
501                 if (as_root)
502                         become_root();
503
504                 if (pass) {
505                         ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
506                 } else {
507                         ret = smb_pam_passchange(name, oldpass, newpass);
508                 }
509                         
510                 if (as_root)
511                         unbecome_root();
512
513                 return ret;
514         }
515 #endif
516
517         /* A non-PAM password change just doen't make sense without a valid local user */
518
519         if (pass == NULL) {
520                 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
521                 return False;
522         }
523
524         pstrcpy(passwordprogram, lp_passwd_program());
525         pstrcpy(chatsequence, lp_passwd_chat());
526
527         if (!*chatsequence) {
528                 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
529                 return (False);
530         }
531
532         if (!*passwordprogram) {
533                 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
534                 return (False);
535         }
536
537         if (as_root) {
538                 /* The password program *must* contain the user name to work. Fail if not. */
539                 if (strstr_m(passwordprogram, "%u") == NULL) {
540                         DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
541 the string %%u, and the given string %s does not.\n", passwordprogram ));
542                         return False;
543                 }
544         }
545
546         pstring_sub(passwordprogram, "%u", name);
547         /* note that we do NOT substitute the %o and %n in the password program
548            as this would open up a security hole where the user could use
549            a new password containing shell escape characters */
550
551         pstring_sub(chatsequence, "%u", name);
552         all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
553         all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
554         return (chat_with_program
555                 (passwordprogram, pass, chatsequence, as_root));
556 }
557
558 #else /* ALLOW_CHANGE_PASSWORD */
559
560 bool chgpasswd(const char *name, const struct passwd *pass, 
561                const char *oldpass, const char *newpass, bool as_root)
562 {
563         DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
564         return (False);
565 }
566 #endif /* ALLOW_CHANGE_PASSWORD */
567
568 /***********************************************************
569  Code to check the lanman hashed password.
570 ************************************************************/
571
572 bool check_lanman_password(char *user, uchar * pass1,
573                            uchar * pass2, struct samu **hnd)
574 {
575         uchar unenc_new_pw[16];
576         uchar unenc_old_pw[16];
577         struct samu *sampass = NULL;
578         uint32 acct_ctrl;
579         const uint8 *lanman_pw;
580         bool ret;
581
582         if ( !(sampass = samu_new(NULL)) ) {
583                 DEBUG(0, ("samu_new() failed!\n"));
584                 return False;
585         }
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                 TALLOC_FREE(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                 TALLOC_FREE(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                         TALLOC_FREE(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                 TALLOC_FREE(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(struct samu *sampass, uchar *pass2)
645 {
646         static uchar null_pw[16];
647         uchar unenc_new_pw[16];
648         bool ret;
649         uint32 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_last_set_time  (sampass, time(NULL), PDB_CHANGED)) {
692                 TALLOC_FREE(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 = NT_STATUS_IS_OK(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 password_encrypted_with_lm_hash[516], 
709                          const uchar old_lm_hash_encrypted[16],
710                          uchar password_encrypted_with_nt_hash[516], 
711                          const uchar old_nt_hash_encrypted[16],
712                          uint32 *reject_reason)
713 {
714         pstring new_passwd;
715         struct samu *sampass = NULL;
716         NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash, 
717                                                 old_lm_hash_encrypted, 
718                                                 password_encrypted_with_nt_hash, 
719                                                 old_nt_hash_encrypted,
720                                                 &sampass, new_passwd, sizeof(new_passwd));
721         
722         if (!NT_STATUS_IS_OK(nt_status))
723                 return nt_status;
724
725         /* We've already checked the old password here.... */
726         become_root();
727         nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
728         unbecome_root();
729
730         memset(new_passwd, 0, sizeof(new_passwd));
731
732         TALLOC_FREE(sampass);
733
734         return nt_status;
735 }
736
737 /***********************************************************
738  Decrypt and verify a user password change.  
739
740  The 516 byte long buffers are encrypted with the old NT and 
741  old LM passwords, and if the NT passwords are present, both 
742  buffers contain a unicode string.
743
744  After decrypting the buffers, check the password is correct by
745  matching the old hashed passwords with the passwords in the passdb.
746  
747 ************************************************************/
748
749 static NTSTATUS check_oem_password(const char *user,
750                                    uchar password_encrypted_with_lm_hash[516], 
751                                    const uchar old_lm_hash_encrypted[16],
752                                    uchar password_encrypted_with_nt_hash[516], 
753                                    const uchar old_nt_hash_encrypted[16],
754                                    struct samu **hnd, char *new_passwd,
755                                    int new_passwd_size)
756 {
757         static uchar null_pw[16];
758         static uchar null_ntpw[16];
759         struct samu *sampass = NULL;
760         uint8 *password_encrypted;
761         const uint8 *encryption_key;
762         const uint8 *lanman_pw, *nt_pw;
763         uint32 acct_ctrl;
764         uint32 new_pw_len;
765         uchar new_nt_hash[16];
766         uchar new_lm_hash[16];
767         uchar verifier[16];
768         char no_pw[2];
769         bool ret;
770
771         bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
772         bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
773
774         *hnd = NULL;
775
776         if ( !(sampass = samu_new( NULL )) ) {
777                 return NT_STATUS_NO_MEMORY;
778         }
779
780         become_root();
781         ret = pdb_getsampwnam(sampass, user);
782         unbecome_root();
783
784         if (ret == False) {
785                 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
786                 TALLOC_FREE(sampass);
787                 return NT_STATUS_NO_SUCH_USER; 
788         }
789
790         acct_ctrl = pdb_get_acct_ctrl(sampass);
791         
792         if (acct_ctrl & ACB_DISABLED) {
793                 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
794                 TALLOC_FREE(sampass);
795                 return NT_STATUS_ACCOUNT_DISABLED;
796         }
797
798         if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
799                 /* construct a null password (in case one is needed */
800                 no_pw[0] = 0;
801                 no_pw[1] = 0;
802                 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
803                 lanman_pw = null_pw;
804                 nt_pw = null_pw;
805
806         } else {
807                 /* save pointers to passwords so we don't have to keep looking them up */
808                 if (lp_lanman_auth()) {
809                         lanman_pw = pdb_get_lanman_passwd(sampass);
810                 } else {
811                         lanman_pw = NULL;
812                 }
813                 nt_pw = pdb_get_nt_passwd(sampass);
814         }
815
816         if (nt_pw && nt_pass_set) {
817                 /* IDEAL Case: passwords are in unicode, and we can
818                  * read use the password encrypted with the NT hash 
819                  */
820                 password_encrypted = password_encrypted_with_nt_hash;
821                 encryption_key = nt_pw;
822         } else if (lanman_pw && lm_pass_set) {
823                 /* password may still be in unicode, but use LM hash version */
824                 password_encrypted = password_encrypted_with_lm_hash;
825                 encryption_key = lanman_pw;
826         } else if (nt_pass_set) {
827                 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n", 
828                           user));
829                 TALLOC_FREE(sampass);
830                 return NT_STATUS_WRONG_PASSWORD;        
831         } else if (lm_pass_set) {
832                 if (lp_lanman_auth()) {
833                         DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n", 
834                                   user));
835                 } else {
836                         DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n", 
837                                   user));
838                 }
839                 TALLOC_FREE(sampass);
840                 return NT_STATUS_WRONG_PASSWORD;
841         } else {
842                 DEBUG(1, ("password change requested for user %s, but no password supplied!\n", 
843                           user));
844                 TALLOC_FREE(sampass);
845                 return NT_STATUS_WRONG_PASSWORD;
846         }
847
848         /* 
849          * Decrypt the password with the key 
850          */
851         SamOEMhash( password_encrypted, encryption_key, 516);
852
853         if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len, 
854                                nt_pass_set ? STR_UNICODE : STR_ASCII)) {
855                 TALLOC_FREE(sampass);
856                 return NT_STATUS_WRONG_PASSWORD;
857         }
858
859         /*
860          * To ensure we got the correct new password, hash it and
861          * use it as a key to test the passed old password.
862          */
863
864         if (nt_pass_set) {
865                 /* NT passwords, verify the NT hash. */
866                 
867                 /* Calculate the MD4 hash (NT compatible) of the password */
868                 memset(new_nt_hash, '\0', 16);
869                 E_md4hash(new_passwd, new_nt_hash);
870
871                 if (nt_pw) {
872                         /*
873                          * check the NT verifier
874                          */
875                         E_old_pw_hash(new_nt_hash, nt_pw, verifier);
876                         if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
877                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
878                                 TALLOC_FREE(sampass);
879                                 return NT_STATUS_WRONG_PASSWORD;
880                         }
881                         
882                         /* We could check the LM password here, but there is
883                          * little point, we already know the password is
884                          * correct, and the LM password might not even be
885                          * present. */
886
887                         /* Further, LM hash generation algorithms
888                          * differ with charset, so we could
889                          * incorrectly fail a perfectly valid password
890                          * change */
891 #ifdef DEBUG_PASSWORD
892                         DEBUG(100,
893                               ("check_oem_password: password %s ok\n", new_passwd));
894 #endif
895                         *hnd = sampass;
896                         return NT_STATUS_OK;
897                 }
898                 
899                 if (lanman_pw) {
900                         /*
901                          * check the lm verifier
902                          */
903                         E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
904                         if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
905                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
906                                 TALLOC_FREE(sampass);
907                                 return NT_STATUS_WRONG_PASSWORD;
908                         }
909 #ifdef DEBUG_PASSWORD
910                         DEBUG(100,
911                               ("check_oem_password: password %s ok\n", new_passwd));
912 #endif
913                         *hnd = sampass;
914                         return NT_STATUS_OK;
915                 }
916         }
917
918         if (lanman_pw && lm_pass_set) {
919
920                 E_deshash(new_passwd, new_lm_hash);
921
922                 /*
923                  * check the lm verifier
924                  */
925                 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
926                 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
927                         DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
928                         TALLOC_FREE(sampass);
929                         return NT_STATUS_WRONG_PASSWORD;
930                 }
931                 
932 #ifdef DEBUG_PASSWORD
933                 DEBUG(100,
934                       ("check_oem_password: password %s ok\n", new_passwd));
935 #endif
936                 *hnd = sampass;
937                 return NT_STATUS_OK;
938         }
939
940         /* should not be reached */
941         TALLOC_FREE(sampass);
942         return NT_STATUS_WRONG_PASSWORD;
943 }
944
945 /***********************************************************
946  This routine takes the given password and checks it against
947  the password history. Returns True if this password has been
948  found in the history list.
949 ************************************************************/
950
951 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
952 {
953         uchar new_nt_p16[NT_HASH_LEN];
954         uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
955         const uint8 *nt_pw;
956         const uint8 *pwhistory;
957         bool found = False;
958         int i;
959         uint32 pwHisLen, curr_pwHisLen;
960
961         pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
962         if (pwHisLen == 0) {
963                 return False;
964         }
965
966         pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
967         if (!pwhistory || curr_pwHisLen == 0) {
968                 return False;
969         }
970
971         /* Only examine the minimum of the current history len and
972            the stored history len. Avoids race conditions. */
973         pwHisLen = MIN(pwHisLen,curr_pwHisLen);
974
975         nt_pw = pdb_get_nt_passwd(sampass);
976
977         E_md4hash(plaintext, new_nt_p16);
978
979         if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
980                 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
981                         pdb_get_username(sampass) ));
982                 return True;
983         }
984
985         dump_data(100, new_nt_p16, NT_HASH_LEN);
986         dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
987
988         memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
989         for (i=0; i<pwHisLen; i++) {
990                 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
991                 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
992                 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
993                                                         PW_HISTORY_SALT_LEN];
994                 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
995                         /* Ignore zero valued entries. */
996                         continue;
997                 }
998                 /* Create salted versions of new to compare. */
999                 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1000
1001                 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1002                         DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1003                                 pdb_get_username(sampass) ));
1004                         found = True;
1005                         break;
1006                 }
1007         }
1008         return found;
1009 }
1010
1011 /***********************************************************
1012  Code to change the oem password. Changes both the lanman
1013  and NT hashes.  Old_passwd is almost always NULL.
1014  NOTE this function is designed to be called as root. Check the old password
1015  is correct before calling. JRA.
1016 ************************************************************/
1017
1018 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
1019 {
1020         uint32 min_len;
1021         uint32 refuse;
1022         struct passwd *pass = NULL;
1023         const char *username = pdb_get_username(hnd);
1024         time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1025
1026         if (samr_reject_reason) {
1027                 *samr_reject_reason = Undefined;
1028         }
1029
1030         /* check to see if the secdesc has previously been set to disallow */
1031         if (!pdb_get_pass_can_change(hnd)) {
1032                 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1033                 if (samr_reject_reason) {
1034                         *samr_reject_reason = REJECT_REASON_OTHER;
1035                 }
1036                 return NT_STATUS_ACCOUNT_RESTRICTION;
1037         }
1038
1039         /* check to see if it is a Machine account and if the policy
1040          * denies machines to change the password. *
1041          * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1042         if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1043                 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1044                         DEBUG(1, ("Machine %s cannot change password now, "
1045                                   "denied by Refuse Machine Password Change policy\n",
1046                                   username));
1047                         if (samr_reject_reason) {
1048                                 *samr_reject_reason = REJECT_REASON_OTHER;
1049                         }
1050                         return NT_STATUS_ACCOUNT_RESTRICTION;
1051                 }
1052         }
1053
1054         /* removed calculation here, becuase passdb now calculates
1055            based on policy.  jmcd */
1056         if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1057                 DEBUG(1, ("user %s cannot change password now, must "
1058                           "wait until %s\n", username,
1059                           http_timestring(can_change_time)));
1060                 if (samr_reject_reason) {
1061                         *samr_reject_reason = REJECT_REASON_OTHER;
1062                 }
1063                 return NT_STATUS_ACCOUNT_RESTRICTION;
1064         }
1065
1066         if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1067                 DEBUG(1, ("user %s cannot change password - password too short\n", 
1068                           username));
1069                 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1070                 if (samr_reject_reason) {
1071                         *samr_reject_reason = REJECT_REASON_TOO_SHORT;
1072                 }
1073                 return NT_STATUS_PASSWORD_RESTRICTION;
1074 /*              return NT_STATUS_PWD_TOO_SHORT; */
1075         }
1076
1077         if (check_passwd_history(hnd,new_passwd)) {
1078                 if (samr_reject_reason) {
1079                         *samr_reject_reason = REJECT_REASON_IN_HISTORY;
1080                 }
1081                 return NT_STATUS_PASSWORD_RESTRICTION;
1082         }
1083
1084         pass = Get_Pwnam(username);
1085         if (!pass) {
1086                 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1087                 return NT_STATUS_ACCESS_DENIED;
1088         }
1089
1090         /* Use external script to check password complexity */
1091         if (lp_check_password_script() && *(lp_check_password_script())) {
1092                 int check_ret;
1093
1094                 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1095                 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1096
1097                 if (check_ret != 0) {
1098                         DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1099                         if (samr_reject_reason) {
1100                                 *samr_reject_reason = REJECT_REASON_NOT_COMPLEX;
1101                         }
1102                         return NT_STATUS_PASSWORD_RESTRICTION;
1103                 }
1104         }
1105
1106         /*
1107          * If unix password sync was requested, attempt to change
1108          * the /etc/passwd database first. Return failure if this cannot
1109          * be done.
1110          *
1111          * This occurs before the oem change, because we don't want to
1112          * update it if chgpasswd failed.
1113          *
1114          * Conditional on lp_unix_password_sync() because we don't want
1115          * to touch the unix db unless we have admin permission.
1116          */
1117         
1118         if(lp_unix_password_sync() &&
1119                 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1120                 return NT_STATUS_ACCESS_DENIED;
1121         }
1122
1123         if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1124                 return NT_STATUS_ACCESS_DENIED;
1125         }
1126
1127         /* Now write it into the file. */
1128         return pdb_update_sam_account (hnd);
1129 }