2 Unix SMB/Netbios implementation.
4 Samba utility functions
5 Copyright (C) Andrew Tridgell 1992-1998
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.
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.
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.
22 /* fork a child process to exec passwd and write to its
23 * tty to change a users password. This is running as the
24 * user who is attempting to change the password.
28 * This code was copied/borrowed and stolen from various sources.
29 * The primary source was the poppasswd.c from the authors of POPMail. This software
30 * was included as a client to change passwords using the 'passwd' program
31 * on the remote machine.
33 * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
34 * is defined in the compiler directives located in the Makefile.
36 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
37 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
38 * and rights to modify, distribute or incorporate this change to the CAP suite or
39 * using it for any other reason are granted, so long as this disclaimer is left intact.
43 This code was hacked considerably for inclusion in Samba, primarily
44 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
45 of the "password chat" option, which allows the easy runtime
46 specification of the expected sequence of events to change a
52 extern int DEBUGLEVEL;
54 #if ALLOW_CHANGE_PASSWORD
55 #define MINPASSWDLENGTH 5
58 static int findpty(char **slave)
65 #endif /* !HAVE_GRANTPT */
67 #if defined(HAVE_GRANTPT)
68 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 1) {
71 *slave = ptsname(master);
73 DEBUG(0,("findpty: Unable to create master/slave pty pair.\n"));
74 /* Stop fd leak on error. */
78 DEBUG(10, ("findpty: Allocated slave pty %s\n", *slave));
82 #else /* HAVE_GRANTPT */
83 fstrcpy( line, "/dev/ptyXX" );
85 dirp = OpenDir(NULL, "/dev", False);
88 while ((dpname = ReadDirName(dirp)) != NULL) {
89 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
90 DEBUG(3,("pty: try to open %s, line was %s\n", dpname, line ) );
93 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
94 DEBUG(3,("pty: opened %s\n", line ) );
103 #endif /* HAVE_GRANTPT */
107 static int dochild(int master,char *slavedev, char *name, char *passwordprogram, BOOL as_root)
110 struct termios stermios;
111 struct passwd *pass = Get_Pwnam(name,True);
116 DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n",
123 #ifdef HAVE_SETRESUID
129 /* Start new session - gets rid of controlling terminal. */
131 DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
135 /* Open slave pty and acquire as new controlling terminal. */
136 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0) {
137 DEBUG(3,("More weirdness, could not open %s\n",
142 ioctl(slave, I_PUSH, "ptem");
143 ioctl(slave, I_PUSH, "ldterm");
144 #elif defined(TIOCSCTTY)
145 if (ioctl(slave,TIOCSCTTY,0) <0) {
146 DEBUG(3,("Error in ioctl call for slave pty\n"));
154 /* Make slave stdin/out/err of child. */
156 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
157 DEBUG(3,("Could not re-direct stdin\n"));
160 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
161 DEBUG(3,("Could not re-direct stdout\n"));
164 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
165 DEBUG(3,("Could not re-direct stderr\n"));
168 if (slave > 2) close(slave);
170 /* Set proper terminal attributes - no echo, canonical input processing,
171 no map NL to CR/NL on output. */
173 if (tcgetattr(0, &stermios) < 0) {
174 DEBUG(3,("could not read default terminal attributes on pty\n"));
177 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
178 stermios.c_lflag |= ICANON;
179 stermios.c_oflag &= ~(ONLCR);
180 if (tcsetattr(0, TCSANOW, &stermios) < 0) {
181 DEBUG(3,("could not set attributes of pty\n"));
185 /* make us completely into the right uid */
187 #ifdef HAVE_SETRESUID
190 setresgid(gid,gid,gid);
191 setresuid(uid,uid,uid);
202 DEBUG(10, ("Invoking '%s' as password change program.\n", passwordprogram));
204 /* execl() password-change application */
205 if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
206 DEBUG(3,("Bad status returned from %s\n",passwordprogram));
212 static int expect(int master,char *expected,char *buf)
219 if (n >= BUFSIZE-1) {
223 /* allow 4 seconds for some output to appear */
224 m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000);
234 pstrcpy(s2,expected);
235 if (do_match(s1, s2, False))
241 static void pwd_sub(char *buf)
243 string_sub(buf,"\\n","\n");
244 string_sub(buf,"\\r","\r");
245 string_sub(buf,"\\s"," ");
246 string_sub(buf,"\\t","\t");
249 static void writestring(int fd,char *s)
258 static int talktochild(int master, char *chatsequence)
262 char *ptr=chatsequence;
268 while (next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) {
272 if (!strequal(chatbuf,"."))
273 ok = expect(master,chatbuf,buf);
275 if (lp_passwd_chat_debug())
276 DEBUG(100,("talktochild: chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
279 DEBUG(3,("response %d incorrect\n",count));
283 if (!next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) break;
285 if (!strequal(chatbuf,"."))
286 writestring(master,chatbuf);
288 if (lp_passwd_chat_debug())
289 DEBUG(100,("talktochild: sendbuf=[%s]\n",chatbuf));
292 if (count<1) return(False);
298 static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root)
306 /* allocate a pseudo-terminal device */
307 if ((master = findpty (&slavedev)) < 0) {
308 DEBUG(3,("Cannot Allocate pty for password change: %s\n",name));
312 if ((pid = fork()) < 0) {
313 DEBUG(3,("Cannot fork() child for password change: %s\n",name));
318 /* we now have a pty */
319 if (pid > 0){ /* This is the parent process */
320 if ((chstat = talktochild(master, chatsequence)) == False) {
321 DEBUG(3,("Child failed to change password: %s\n",name));
322 kill(pid, SIGKILL); /* be sure to end this process */
325 if ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
326 DEBUG(3,("The process is no longer waiting!\n\n"));
334 DEBUG(3,("We were waiting for the wrong process ID\n"));
337 if (WIFEXITED(wstat) == 0) {
338 DEBUG(3,("The process exited while we were waiting\n"));
341 if (WEXITSTATUS(wstat) != 0) {
342 DEBUG(3,("The status of the process exiting was %d\n", wstat));
350 * Lose any oplock capabilities.
352 set_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
353 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
355 /* make sure it doesn't freeze */
360 DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid()));
361 chstat = dochild(master, slavedev, name, passwordprogram, as_root);
364 unbecome_root(False);
368 DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
373 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
375 pstring passwordprogram;
376 pstring chatsequence;
381 DEBUG(3,("Password change for user: %s\n",name));
384 DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass));
387 /* Take the passed information and test it for minimum criteria */
388 /* Minimum password length */
389 if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */
391 DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
392 return (False); /* inform the user */
395 /* Password is same as old password */
396 if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
398 DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
399 return (False); /* inform the user */
402 pstrcpy(passwordprogram,lp_passwd_program());
403 pstrcpy(chatsequence,lp_passwd_chat());
405 if (!*chatsequence) {
406 DEBUG(2,("Null chat sequence - no password changing\n"));
410 if (!*passwordprogram) {
411 DEBUG(2,("Null password program - no password changing\n"));
416 * Check the old and new passwords don't contain any control
420 len = strlen(oldpass);
421 for(i = 0; i < len; i++) {
422 if (iscntrl((int)oldpass[i])) {
423 DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n"));
428 len = strlen(newpass);
429 for(i = 0; i < len; i++) {
430 if (iscntrl((int)newpass[i])) {
431 DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n"));
436 string_sub(passwordprogram,"%u",name);
437 all_string_sub(passwordprogram,"%o",oldpass);
438 all_string_sub(passwordprogram,"%n",newpass);
440 string_sub(chatsequence,"%u",name);
441 all_string_sub(chatsequence,"%o",oldpass);
442 all_string_sub(chatsequence,"%n",newpass);
443 return(chat_with_program(passwordprogram,name,chatsequence, as_root));
446 #else /* ALLOW_CHANGE_PASSWORD */
447 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
449 DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
452 #endif /* ALLOW_CHANGE_PASSWORD */
454 /***********************************************************
455 Code to check the lanman hashed password.
456 ************************************************************/
458 BOOL check_lanman_password(char *user, uchar *pass1,
459 uchar *pass2, struct smb_passwd **psmbpw)
461 static uchar null_pw[16];
462 uchar unenc_new_pw[16];
463 uchar unenc_old_pw[16];
464 struct smb_passwd *smbpw;
469 smbpw = getsmbpwnam(user);
474 DEBUG(0,("check_lanman_password: getsmbpwnam returned NULL\n"));
478 if (smbpw->acct_ctrl & ACB_DISABLED)
480 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
484 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
487 memset(no_pw, '\0', 14);
488 E_P16(no_pw, null_pw);
489 smbpw->smb_passwd = null_pw;
490 } else if (smbpw->smb_passwd == NULL) {
491 DEBUG(0,("check_lanman_password: no lanman password !\n"));
495 /* Get the new lanman hash. */
496 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
498 /* Use this to get the old lanman hash. */
499 D_P16(unenc_new_pw, pass1, unenc_old_pw);
501 /* Check that the two old passwords match. */
502 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
504 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
512 /***********************************************************
513 Code to change the lanman hashed password.
514 It nulls out the NT hashed password as it will
516 ************************************************************/
518 BOOL change_lanman_password(struct smb_passwd *smbpw, uchar *pass1, uchar *pass2)
520 static uchar null_pw[16];
521 uchar unenc_new_pw[16];
526 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
530 if (smbpw->acct_ctrl & ACB_DISABLED)
532 DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->unix_name));
536 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
539 memset(no_pw, '\0', 14);
540 E_P16(no_pw, null_pw);
541 smbpw->smb_passwd = null_pw;
542 } else if (smbpw->smb_passwd == NULL) {
543 DEBUG(0,("change_lanman_password: no lanman password !\n"));
547 /* Get the new lanman hash. */
548 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
550 smbpw->smb_passwd = unenc_new_pw;
551 smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */
553 /* Now write it into the file. */
555 ret = mod_smbpwd_entry(smbpw,False);
561 /***********************************************************
562 Code to check and change the OEM hashed password.
563 ************************************************************/
564 BOOL pass_oem_change(char *user,
565 uchar *lmdata, uchar *lmhash,
566 uchar *ntdata, uchar *nthash)
569 struct smb_passwd *sampw;
570 BOOL ret = check_oem_password( user, lmdata, lmhash, ntdata, nthash,
572 new_passwd, sizeof(new_passwd));
574 /* now we check to see if we are actually allowed to change the
577 if (ret && (sampw->acct_ctrl & ACB_PWLOCK))
583 * At this point we have the new case-sensitive plaintext
584 * password in the fstring new_passwd. If we wanted to synchronise
585 * with UNIX passwords we would call a UNIX password changing
586 * function here. However it would have to be done as root
587 * as the plaintext of the old users password is not
591 if ( ret && lp_unix_password_sync())
593 ret = chgpasswd(user,"", new_passwd, True);
598 ret = change_oem_password( sampw, new_passwd, False );
601 memset(new_passwd, 0, sizeof(new_passwd));
606 /***********************************************************
607 decode a password buffer
608 ************************************************************/
609 BOOL decode_pw_buffer(const char buffer[516], char *new_passwd,
610 int new_passwd_size, BOOL nt_pass_set)
613 * The length of the new password is in the last 4 bytes of
617 uint32 new_pw_len = IVAL(buffer, 512);
618 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1)
620 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
627 * nt passwords are in unicode
629 int uni_pw_len = new_pw_len;
631 unibuf_to_ascii(new_passwd, &buffer[512-uni_pw_len], new_pw_len);
635 memcpy(new_passwd, &buffer[512-new_pw_len], new_pw_len);
636 new_passwd[new_pw_len] = '\0';
642 /***********************************************************
643 Code to check the OEM hashed password.
645 this function ignores the 516 byte nt OEM hashed password
646 but does use the lm OEM password to check the nt hashed-hash.
648 ************************************************************/
649 BOOL check_oem_password(char *user,
650 uchar *lmdata, uchar *lmhash,
651 uchar *ntdata, uchar *nthash,
652 struct smb_passwd **psmbpw, char *new_passwd,
655 static uchar null_pw[16];
656 static uchar null_ntpw[16];
657 struct smb_passwd *smbpw = NULL;
659 uchar unenc_old_ntpw[16];
661 uchar unenc_old_pw[16];
664 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
667 *psmbpw = smbpw = getsmbpwnam(user);
668 unbecome_root(False);
672 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
676 if (smbpw->acct_ctrl & ACB_DISABLED)
678 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
682 /* construct a null password (in case one is needed */
685 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
687 /* check for null passwords */
688 if (smbpw->smb_passwd == NULL)
690 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
692 smbpw->smb_passwd = null_pw;
696 DEBUG(0,("check_oem_password: no lanman password !\n"));
701 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
703 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
705 smbpw->smb_nt_passwd = null_pw;
709 DEBUG(0,("check_oem_password: no ntlm password !\n"));
715 * Call the hash function to get the new password.
717 SamOEMhash( (uchar *)lmdata, (uchar *)smbpw->smb_passwd, True);
719 if (!decode_pw_buffer(lmdata, new_passwd, new_passwd_size, nt_pass_set))
725 * To ensure we got the correct new password, hash it and
726 * use it as a key to test the passed old password.
729 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
734 * Now use new_p16 as the key to see if the old
737 D_P16(new_p16 , lmhash, unenc_old_pw);
739 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
741 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
745 #ifdef DEBUG_PASSWORD
746 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
752 * Now use new_p16 as the key to see if the old
755 D_P16(new_ntp16, lmhash, unenc_old_pw);
756 D_P16(new_ntp16, nthash, unenc_old_ntpw);
758 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
760 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
764 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
766 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
769 #ifdef DEBUG_PASSWORD
770 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
775 /***********************************************************
776 Code to change the oem password. Changes both the lanman
778 override = False, normal
779 override = True, override XXXXXXXXXX'd password
780 ************************************************************/
782 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd, BOOL override)
785 uchar new_nt_p16[16];
788 nt_lm_owf_gen(new_passwd, new_nt_p16, new_p16);
790 smbpw->smb_passwd = new_p16;
791 smbpw->smb_nt_passwd = new_nt_p16;
793 /* Now write it into the file. */
795 ret = mod_smbpwd_entry(smbpw,override);
798 memset(new_passwd, '\0', strlen(new_passwd));