Rework Samba3 to use new libcli/auth code (partial)
[gd/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 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 #include "../libcli/auth/libcli_auth.h"
50
51 static NTSTATUS check_oem_password(const char *user,
52                                    uchar password_encrypted_with_lm_hash[516],
53                                    const uchar old_lm_hash_encrypted[16],
54                                    uchar password_encrypted_with_nt_hash[516],
55                                    const uchar old_nt_hash_encrypted[16],
56                                    struct samu **hnd,
57                                    char **pp_new_passwd);
58
59 #if ALLOW_CHANGE_PASSWORD
60
61 static int findpty(char **slave)
62 {
63         int master = -1;
64         char *line = NULL;
65         SMB_STRUCT_DIR *dirp = NULL;
66         const char *dpname;
67
68         *slave = NULL;
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                 grantpt(master);
74                 unlockpt(master);
75                 line = (char *)ptsname(master);
76                 if (line) {
77                         *slave = SMB_STRDUP(line);
78                 }
79
80                 if (*slave == NULL) {
81                         DEBUG(0,
82                               ("findpty: Unable to create master/slave pty pair.\n"));
83                         /* Stop fd leak on error. */
84                         close(master);
85                         return -1;
86                 } else {
87                         DEBUG(10,
88                               ("findpty: Allocated slave pty %s\n", *slave));
89                         return (master);
90                 }
91         }
92 #endif /* HAVE_GRANTPT */
93
94         line = SMB_STRDUP("/dev/ptyXX");
95         if (!line) {
96                 return (-1);
97         }
98
99         dirp = sys_opendir("/dev");
100         if (!dirp) {
101                 SAFE_FREE(line);
102                 return (-1);
103         }
104
105         while ((dpname = readdirname(dirp)) != NULL) {
106                 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
107                         DEBUG(3,
108                               ("pty: try to open %s, line was %s\n", dpname,
109                                line));
110                         line[8] = dpname[3];
111                         line[9] = dpname[4];
112                         if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
113                                 DEBUG(3, ("pty: opened %s\n", line));
114                                 line[5] = 't';
115                                 *slave = line;
116                                 sys_closedir(dirp);
117                                 return (master);
118                         }
119                 }
120         }
121         sys_closedir(dirp);
122         SAFE_FREE(line);
123         return (-1);
124 }
125
126 static int dochild(int master, const char *slavedev, const struct passwd *pass,
127                    const char *passwordprogram, bool as_root)
128 {
129         int slave;
130         struct termios stermios;
131         gid_t gid;
132         uid_t uid;
133         char * const eptrs[1] = { NULL };
134
135         if (pass == NULL)
136         {
137                 DEBUG(0,
138                       ("dochild: user doesn't exist in the UNIX password database.\n"));
139                 return False;
140         }
141
142         gid = pass->pw_gid;
143         uid = pass->pw_uid;
144
145         gain_root_privilege();
146
147         /* Start new session - gets rid of controlling terminal. */
148         if (setsid() < 0)
149         {
150                 DEBUG(3,
151                       ("Weirdness, couldn't let go of controlling terminal\n"));
152                 return (False);
153         }
154
155         /* Open slave pty and acquire as new controlling terminal. */
156         if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
157         {
158                 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
159                 return (False);
160         }
161 #if defined(TIOCSCTTY) && !defined(SUNOS5)
162         /*
163          * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
164          * see the discussion under
165          * https://bugzilla.samba.org/show_bug.cgi?id=5366.
166          */
167         if (ioctl(slave, TIOCSCTTY, 0) < 0)
168         {
169                 DEBUG(3, ("Error in ioctl call for slave pty\n"));
170                 /* return(False); */
171         }
172 #elif defined(I_PUSH) && defined(I_FIND)
173         if (ioctl(slave, I_FIND, "ptem") == 0) {
174                 ioctl(slave, I_PUSH, "ptem");
175         }
176         if (ioctl(slave, I_FIND, "ldterm") == 0) {
177                 ioctl(slave, I_PUSH, "ldterm");
178         }
179 #endif
180
181         /* Close master. */
182         close(master);
183
184         /* Make slave stdin/out/err of child. */
185
186         if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
187         {
188                 DEBUG(3, ("Could not re-direct stdin\n"));
189                 return (False);
190         }
191         if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
192         {
193                 DEBUG(3, ("Could not re-direct stdout\n"));
194                 return (False);
195         }
196         if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
197         {
198                 DEBUG(3, ("Could not re-direct stderr\n"));
199                 return (False);
200         }
201         if (slave > 2)
202                 close(slave);
203
204         /* Set proper terminal attributes - no echo, canonical input processing,
205            no map NL to CR/NL on output. */
206
207         if (tcgetattr(0, &stermios) < 0)
208         {
209                 DEBUG(3,
210                       ("could not read default terminal attributes on pty\n"));
211                 return (False);
212         }
213         stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
214         stermios.c_lflag |= ICANON;
215 #ifdef ONLCR
216         stermios.c_oflag &= ~(ONLCR);
217 #endif
218         if (tcsetattr(0, TCSANOW, &stermios) < 0)
219         {
220                 DEBUG(3, ("could not set attributes of pty\n"));
221                 return (False);
222         }
223
224         /* make us completely into the right uid */
225         if (!as_root)
226         {
227                 become_user_permanently(uid, gid);
228         }
229
230         DEBUG(10,
231               ("Invoking '%s' as password change program.\n",
232                passwordprogram));
233
234         /* execl() password-change application */
235         if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
236         {
237                 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
238                 return (False);
239         }
240         return (True);
241 }
242
243 static int expect(int master, char *issue, char *expected)
244 {
245         char buffer[1024];
246         int attempts, timeout, nread;
247         size_t len;
248         bool match = False;
249
250         for (attempts = 0; attempts < 2; attempts++) {
251                 NTSTATUS status;
252                 if (!strequal(issue, ".")) {
253                         if (lp_passwd_chat_debug())
254                                 DEBUG(100, ("expect: sending [%s]\n", issue));
255
256                         if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
257                                 DEBUG(2,("expect: (short) write returned %d\n",
258                                          (int)len ));
259                                 return False;
260                         }
261                 }
262
263                 if (strequal(expected, "."))
264                         return True;
265
266                 /* Initial timeout. */
267                 timeout = lp_passwd_chat_timeout() * 1000;
268                 nread = 0;
269                 buffer[nread] = 0;
270
271                 while (True) {
272                         status = read_socket_with_timeout(
273                                 master, buffer + nread, 1,
274                                 sizeof(buffer) - nread - 1,
275                                 timeout, &len);
276
277                         if (!NT_STATUS_IS_OK(status)) {
278                                 break;
279                         }
280                         nread += len;
281                         buffer[nread] = 0;
282
283                         {
284                                 /* Eat leading/trailing whitespace before match. */
285                                 char *str = SMB_STRDUP(buffer);
286                                 if (!str) {
287                                         DEBUG(2,("expect: ENOMEM\n"));
288                                         return False;
289                                 }
290                                 trim_char(str, ' ', ' ');
291
292                                 if ((match = unix_wild_match(expected, str)) == True) {
293                                         /* Now data has started to return, lower timeout. */
294                                         timeout = lp_passwd_chat_timeout() * 100;
295                                 }
296                                 SAFE_FREE(str);
297                         }
298                 }
299
300                 if (lp_passwd_chat_debug())
301                         DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
302                                     expected, buffer, match ? "yes" : "no" ));
303
304                 if (match)
305                         break;
306
307                 if (!NT_STATUS_IS_OK(status)) {
308                         DEBUG(2, ("expect: %s\n", nt_errstr(status)));
309                         return False;
310                 }
311         }
312
313         DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
314         return match;
315 }
316
317 static void pwd_sub(char *buf)
318 {
319         all_string_sub(buf, "\\n", "\n", 0);
320         all_string_sub(buf, "\\r", "\r", 0);
321         all_string_sub(buf, "\\s", " ", 0);
322         all_string_sub(buf, "\\t", "\t", 0);
323 }
324
325 static int talktochild(int master, const char *seq)
326 {
327         TALLOC_CTX *frame = talloc_stackframe();
328         int count = 0;
329         char *issue;
330         char *expected;
331
332         issue = talloc_strdup(frame, ".");
333         if (!issue) {
334                 TALLOC_FREE(frame);
335                 return false;
336         }
337
338         while (next_token_talloc(frame, &seq, &expected, NULL)) {
339                 pwd_sub(expected);
340                 count++;
341
342                 if (!expect(master, issue, expected)) {
343                         DEBUG(3, ("Response %d incorrect\n", count));
344                         TALLOC_FREE(frame);
345                         return false;
346                 }
347
348                 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
349                         issue = talloc_strdup(frame, ".");
350                         if (!issue) {
351                                 TALLOC_FREE(frame);
352                                 return false;
353                         }
354                 }
355                 pwd_sub(issue);
356         }
357
358         if (!strequal(issue, ".")) {
359                 /* we have one final issue to send */
360                 expected = talloc_strdup(frame, ".");
361                 if (!expected) {
362                         TALLOC_FREE(frame);
363                         return false;
364                 }
365                 if (!expect(master, issue, expected)) {
366                         TALLOC_FREE(frame);
367                         return False;
368                 }
369         }
370         TALLOC_FREE(frame);
371         return (count > 0);
372 }
373
374 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
375                               char *chatsequence, bool as_root)
376 {
377         char *slavedev = NULL;
378         int master;
379         pid_t pid, wpid;
380         int wstat;
381         bool chstat = False;
382
383         if (pass == NULL) {
384                 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
385                 return False;
386         }
387
388         /* allocate a pseudo-terminal device */
389         if ((master = findpty(&slavedev)) < 0) {
390                 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
391                 return (False);
392         }
393
394         /*
395          * We need to temporarily stop CatchChild from eating
396          * SIGCLD signals as it also eats the exit status code. JRA.
397          */
398
399         CatchChildLeaveStatus();
400
401         if ((pid = sys_fork()) < 0) {
402                 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
403                 SAFE_FREE(slavedev);
404                 close(master);
405                 CatchChild();
406                 return (False);
407         }
408
409         /* we now have a pty */
410         if (pid > 0) {                  /* This is the parent process */
411                 /* Don't need this anymore in parent. */
412                 SAFE_FREE(slavedev);
413
414                 if ((chstat = talktochild(master, chatsequence)) == False) {
415                         DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
416                         kill(pid, SIGKILL);     /* be sure to end this process */
417                 }
418
419                 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
420                         if (errno == EINTR) {
421                                 errno = 0;
422                                 continue;
423                         }
424                         break;
425                 }
426
427                 if (wpid < 0) {
428                         DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
429                         close(master);
430                         CatchChild();
431                         return (False);
432                 }
433
434                 /*
435                  * Go back to ignoring children.
436                  */
437                 CatchChild();
438
439                 close(master);
440
441                 if (pid != wpid) {
442                         DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
443                         return (False);
444                 }
445                 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
446                         DEBUG(3, ("chat_with_program: The process exited with status %d \
447 while we were waiting\n", WEXITSTATUS(wstat)));
448                         return (False);
449                 }
450 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
451                 else if (WIFSIGNALLED(wstat)) {
452                         DEBUG(3, ("chat_with_program: The process was killed by signal %d \
453 while we were waiting\n", WTERMSIG(wstat)));
454                         return (False);
455                 }
456 #endif
457         } else {
458                 /* CHILD */
459
460                 /*
461                  * Lose any elevated privileges.
462                  */
463                 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
464                 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
465
466                 /* make sure it doesn't freeze */
467                 alarm(20);
468
469                 if (as_root)
470                         become_root();
471
472                 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
473                        (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
474                 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
475
476                 if (as_root)
477                         unbecome_root();
478
479                 /*
480                  * The child should never return from dochild() ....
481                  */
482
483                 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
484                 exit(1);
485         }
486
487         if (chstat)
488                 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
489                        (chstat ? "" : "un"), pass->pw_name));
490         return (chstat);
491 }
492
493 bool chgpasswd(const char *name, const struct passwd *pass,
494                const char *oldpass, const char *newpass, bool as_root)
495 {
496         char *passwordprogram = NULL;
497         char *chatsequence = NULL;
498         size_t i;
499         size_t len;
500         TALLOC_CTX *ctx = talloc_tos();
501
502         if (!oldpass) {
503                 oldpass = "";
504         }
505
506         DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
507
508 #ifdef DEBUG_PASSWORD
509         DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
510 #endif
511
512         /* Take the passed information and test it for minimum criteria */
513
514         /* Password is same as old password */
515         if (strcmp(oldpass, newpass) == 0) {
516                 /* don't allow same password */
517                 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name));      /* log the attempt */
518                 return (False); /* inform the user */
519         }
520
521         /*
522          * Check the old and new passwords don't contain any control
523          * characters.
524          */
525
526         len = strlen(oldpass);
527         for (i = 0; i < len; i++) {
528                 if (iscntrl((int)oldpass[i])) {
529                         DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
530                         return False;
531                 }
532         }
533
534         len = strlen(newpass);
535         for (i = 0; i < len; i++) {
536                 if (iscntrl((int)newpass[i])) {
537                         DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
538                         return False;
539                 }
540         }
541
542 #ifdef WITH_PAM
543         if (lp_pam_password_change()) {
544                 bool ret;
545 #ifdef HAVE_SETLOCALE
546                 const char *prevlocale = setlocale(LC_ALL, "C");
547 #endif
548
549                 if (as_root)
550                         become_root();
551
552                 if (pass) {
553                         ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
554                 } else {
555                         ret = smb_pam_passchange(name, oldpass, newpass);
556                 }
557
558                 if (as_root)
559                         unbecome_root();
560
561 #ifdef HAVE_SETLOCALE
562                 setlocale(LC_ALL, prevlocale);
563 #endif
564
565                 return ret;
566         }
567 #endif
568
569         /* A non-PAM password change just doen't make sense without a valid local user */
570
571         if (pass == NULL) {
572                 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
573                 return false;
574         }
575
576         passwordprogram = talloc_strdup(ctx, lp_passwd_program());
577         if (!passwordprogram || !*passwordprogram) {
578                 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
579                 return false;
580         }
581         chatsequence = talloc_strdup(ctx, lp_passwd_chat());
582         if (!chatsequence || !*chatsequence) {
583                 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
584                 return false;
585         }
586
587         if (as_root) {
588                 /* The password program *must* contain the user name to work. Fail if not. */
589                 if (strstr_m(passwordprogram, "%u") == NULL) {
590                         DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
591 the string %%u, and the given string %s does not.\n", passwordprogram ));
592                         return false;
593                 }
594         }
595
596         passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
597         if (!passwordprogram) {
598                 return false;
599         }
600
601         /* note that we do NOT substitute the %o and %n in the password program
602            as this would open up a security hole where the user could use
603            a new password containing shell escape characters */
604
605         chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
606         if (!chatsequence) {
607                 return false;
608         }
609         chatsequence = talloc_all_string_sub(ctx,
610                                         chatsequence,
611                                         "%o",
612                                         oldpass);
613         if (!chatsequence) {
614                 return false;
615         }
616         chatsequence = talloc_all_string_sub(ctx,
617                                         chatsequence,
618                                         "%n",
619                                         newpass);
620         return chat_with_program(passwordprogram,
621                                 pass,
622                                 chatsequence,
623                                 as_root);
624 }
625
626 #else /* ALLOW_CHANGE_PASSWORD */
627
628 bool chgpasswd(const char *name, const struct passwd *pass, 
629                const char *oldpass, const char *newpass, bool as_root)
630 {
631         DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
632         return (False);
633 }
634 #endif /* ALLOW_CHANGE_PASSWORD */
635
636 /***********************************************************
637  Code to check the lanman hashed password.
638 ************************************************************/
639
640 bool check_lanman_password(char *user, uchar * pass1,
641                            uchar * pass2, struct samu **hnd)
642 {
643         uchar unenc_new_pw[16];
644         uchar unenc_old_pw[16];
645         struct samu *sampass = NULL;
646         uint32 acct_ctrl;
647         const uint8 *lanman_pw;
648         bool ret;
649
650         if ( !(sampass = samu_new(NULL)) ) {
651                 DEBUG(0, ("samu_new() failed!\n"));
652                 return False;
653         }
654         
655         become_root();
656         ret = pdb_getsampwnam(sampass, user);
657         unbecome_root();
658
659         if (ret == False) {
660                 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
661                 TALLOC_FREE(sampass);
662                 return False;
663         }
664         
665         acct_ctrl = pdb_get_acct_ctrl     (sampass);
666         lanman_pw = pdb_get_lanman_passwd (sampass);
667
668         if (acct_ctrl & ACB_DISABLED) {
669                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
670                 TALLOC_FREE(sampass);
671                 return False;
672         }
673
674         if (lanman_pw == NULL) {
675                 if (acct_ctrl & ACB_PWNOTREQ) {
676                         /* this saves the pointer for the caller */
677                         *hnd = sampass;
678                         return True;
679                 } else {
680                         DEBUG(0, ("check_lanman_password: no lanman password !\n"));
681                         TALLOC_FREE(sampass);
682                         return False;
683                 }
684         }
685
686         /* Get the new lanman hash. */
687         D_P16(lanman_pw, pass2, unenc_new_pw);
688
689         /* Use this to get the old lanman hash. */
690         D_P16(unenc_new_pw, pass1, unenc_old_pw);
691
692         /* Check that the two old passwords match. */
693         if (memcmp(lanman_pw, unenc_old_pw, 16)) {
694                 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
695                 TALLOC_FREE(sampass);
696                 return False;
697         }
698
699         /* this saves the pointer for the caller */
700         *hnd = sampass;
701         return True;
702 }
703
704 /***********************************************************
705  Code to change the lanman hashed password.
706  It nulls out the NT hashed password as it will
707  no longer be valid.
708  NOTE this function is designed to be called as root. Check the old password
709  is correct before calling. JRA.
710 ************************************************************/
711
712 bool change_lanman_password(struct samu *sampass, uchar *pass2)
713 {
714         uchar null_pw[16];
715         uchar unenc_new_pw[16];
716         bool ret;
717         uint32 acct_ctrl;
718         const uint8 *pwd;
719
720         if (sampass == NULL) {
721                 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
722                 return False;
723         }
724         
725         acct_ctrl = pdb_get_acct_ctrl(sampass);
726         pwd = pdb_get_lanman_passwd(sampass);
727
728         if (acct_ctrl & ACB_DISABLED) {
729                 DEBUG(0,("change_lanman_password: account %s disabled.\n",
730                        pdb_get_username(sampass)));
731                 return False;
732         }
733
734         if (pwd == NULL) { 
735                 if (acct_ctrl & ACB_PWNOTREQ) {
736                         uchar no_pw[14];
737
738                         ZERO_STRUCT(no_pw);
739
740                         E_P16(no_pw, null_pw);
741
742                         pwd = null_pw;
743                 } else {
744                         DEBUG(0,("change_lanman_password: no lanman password !\n"));
745                         return False;
746                 }
747         }
748
749         /* Get the new lanman hash. */
750         D_P16(pwd, pass2, unenc_new_pw);
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         uchar null_pw[16];
830         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         size_t 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         arcfour_crypt( 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 ? CH_UTF16 : CH_DOS)) {
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(talloc_tos(), 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 }