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