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