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