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