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 = open("/dev/ptmx", O_RDWR)) >= 1) {
71 *slave = ptsname(master);
73 DEBUG(0,("findpty: Unable to create master/slave pty pair.\n"));
76 DEBUG(10, ("findpty: Allocated slave pty %s\n", *slave));
80 #else /* HAVE_GRANTPT */
81 fstrcpy( line, "/dev/ptyXX" );
83 dirp = OpenDir(NULL, "/dev", False);
84 if (!dirp) return(-1);
85 while ((dpname = ReadDirName(dirp)) != NULL) {
86 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
87 DEBUG(3,("pty: try to open %s, line was %s\n", dpname, line ) );
90 if ((master = open(line, O_RDWR)) >= 0) {
91 DEBUG(3,("pty: opened %s\n", line ) );
100 #endif /* HAVE_GRANTPT */
104 static int dochild(int master,char *slavedev, char *name, char *passwordprogram, BOOL as_root)
107 struct termios stermios;
108 struct passwd *pass = Get_Pwnam(name,True);
113 DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n",
120 #ifdef HAVE_SETRESUID
126 /* Start new session - gets rid of controlling terminal. */
128 DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
132 /* Open slave pty and acquire as new controlling terminal. */
133 if ((slave = open(slavedev, O_RDWR)) < 0) {
134 DEBUG(3,("More weirdness, could not open %s\n",
139 ioctl(slave, I_PUSH, "ptem");
140 ioctl(slave, I_PUSH, "ldterm");
141 #elif defined(TIOCSCTTY)
142 if (ioctl(slave,TIOCSCTTY,0) <0) {
143 DEBUG(3,("Error in ioctl call for slave pty\n"));
151 /* Make slave stdin/out/err of child. */
153 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
154 DEBUG(3,("Could not re-direct stdin\n"));
157 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
158 DEBUG(3,("Could not re-direct stdout\n"));
161 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
162 DEBUG(3,("Could not re-direct stderr\n"));
165 if (slave > 2) close(slave);
167 /* Set proper terminal attributes - no echo, canonical input processing,
168 no map NL to CR/NL on output. */
170 if (tcgetattr(0, &stermios) < 0) {
171 DEBUG(3,("could not read default terminal attributes on pty\n"));
174 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
175 stermios.c_lflag |= ICANON;
176 stermios.c_oflag &= ~(ONLCR);
177 if (tcsetattr(0, TCSANOW, &stermios) < 0) {
178 DEBUG(3,("could not set attributes of pty\n"));
182 /* make us completely into the right uid */
184 #ifdef HAVE_SETRESUID
187 setresgid(gid,gid,gid);
188 setresuid(uid,uid,uid);
199 DEBUG(10, ("Invoking '%s' as password change program.\n", passwordprogram));
201 /* execl() password-change application */
202 if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
203 DEBUG(3,("Bad status returned from %s\n",passwordprogram));
209 static int expect(int master,char *expected,char *buf)
216 if (n >= BUFSIZE-1) {
220 /* allow 4 seconds for some output to appear */
221 m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000);
231 pstrcpy(s2,expected);
232 if (do_match(s1, s2, False))
238 static void pwd_sub(char *buf)
240 string_sub(buf,"\\n","\n");
241 string_sub(buf,"\\r","\r");
242 string_sub(buf,"\\s"," ");
243 string_sub(buf,"\\t","\t");
246 static void writestring(int fd,char *s)
255 static int talktochild(int master, char *chatsequence)
259 char *ptr=chatsequence;
265 while (next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) {
269 if (!strequal(chatbuf,"."))
270 ok = expect(master,chatbuf,buf);
272 if (lp_passwd_chat_debug())
273 DEBUG(100,("talktochild: chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
276 DEBUG(3,("response %d incorrect\n",count));
280 if (!next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) break;
282 if (!strequal(chatbuf,"."))
283 writestring(master,chatbuf);
285 if (lp_passwd_chat_debug())
286 DEBUG(100,("talktochild: sendbuf=[%s]\n",chatbuf));
289 if (count<1) return(False);
295 static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root)
303 /* allocate a pseudo-terminal device */
304 if ((master = findpty (&slavedev)) < 0) {
305 DEBUG(3,("Cannot Allocate pty for password change: %s\n",name));
309 if ((pid = fork()) < 0) {
310 DEBUG(3,("Cannot fork() child for password change: %s\n",name));
315 /* we now have a pty */
316 if (pid > 0){ /* This is the parent process */
317 if ((chstat = talktochild(master, chatsequence)) == False) {
318 DEBUG(3,("Child failed to change password: %s\n",name));
319 kill(pid, SIGKILL); /* be sure to end this process */
322 if ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
323 DEBUG(3,("The process is no longer waiting!\n\n"));
331 DEBUG(3,("We were waiting for the wrong process ID\n"));
334 if (WIFEXITED(wstat) == 0) {
335 DEBUG(3,("The process exited while we were waiting\n"));
338 if (WEXITSTATUS(wstat) != 0) {
339 DEBUG(3,("The status of the process exiting was %d\n", wstat));
347 * Lose any oplock capabilities.
349 set_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
350 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
352 /* make sure it doesn't freeze */
357 DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid()));
358 chstat = dochild(master, slavedev, name, passwordprogram, as_root);
361 unbecome_root(False);
365 DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
370 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
372 pstring passwordprogram;
373 pstring chatsequence;
378 DEBUG(3,("Password change for user: %s\n",name));
381 DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass));
384 /* Take the passed information and test it for minimum criteria */
385 /* Minimum password length */
386 if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */
388 DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
389 return (False); /* inform the user */
392 /* Password is same as old password */
393 if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
395 DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
396 return (False); /* inform the user */
399 pstrcpy(passwordprogram,lp_passwd_program());
400 pstrcpy(chatsequence,lp_passwd_chat());
402 if (!*chatsequence) {
403 DEBUG(2,("Null chat sequence - no password changing\n"));
407 if (!*passwordprogram) {
408 DEBUG(2,("Null password program - no password changing\n"));
413 * Check the old and new passwords don't contain any control
417 len = strlen(oldpass);
418 for(i = 0; i < len; i++) {
419 if (iscntrl((int)oldpass[i])) {
420 DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n"));
425 len = strlen(newpass);
426 for(i = 0; i < len; i++) {
427 if (iscntrl((int)newpass[i])) {
428 DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n"));
433 string_sub(passwordprogram,"%u",name);
434 string_sub(passwordprogram,"%o",oldpass);
435 string_sub(passwordprogram,"%n",newpass);
437 string_sub(chatsequence,"%u",name);
438 string_sub(chatsequence,"%o",oldpass);
439 string_sub(chatsequence,"%n",newpass);
440 return(chat_with_program(passwordprogram,name,chatsequence, as_root));
443 #else /* ALLOW_CHANGE_PASSWORD */
444 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
446 DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
449 #endif /* ALLOW_CHANGE_PASSWORD */
451 /***********************************************************
452 Code to check the lanman hashed password.
453 ************************************************************/
455 BOOL check_lanman_password(char *user, uchar *pass1,
456 uchar *pass2, struct smb_passwd **psmbpw)
458 static uchar null_pw[16];
459 uchar unenc_new_pw[16];
460 uchar unenc_old_pw[16];
461 struct smb_passwd *smbpw;
466 smbpw = getsmbpwnam(user);
471 DEBUG(0,("check_lanman_password: getsmbpwnam returned NULL\n"));
475 if (smbpw->acct_ctrl & ACB_DISABLED)
477 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
481 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
484 memset(no_pw, '\0', 14);
485 E_P16(no_pw, null_pw);
486 smbpw->smb_passwd = null_pw;
487 } else if (smbpw->smb_passwd == NULL) {
488 DEBUG(0,("check_lanman_password: no lanman password !\n"));
492 /* Get the new lanman hash. */
493 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
495 /* Use this to get the old lanman hash. */
496 D_P16(unenc_new_pw, pass1, unenc_old_pw);
498 /* Check that the two old passwords match. */
499 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
501 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
509 /***********************************************************
510 Code to change the lanman hashed password.
511 It nulls out the NT hashed password as it will
513 ************************************************************/
515 BOOL change_lanman_password(struct smb_passwd *smbpw, uchar *pass1, uchar *pass2)
517 static uchar null_pw[16];
518 uchar unenc_new_pw[16];
523 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
527 if (smbpw->acct_ctrl & ACB_DISABLED)
529 DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->smb_name));
533 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
536 memset(no_pw, '\0', 14);
537 E_P16(no_pw, null_pw);
538 smbpw->smb_passwd = null_pw;
539 } else if (smbpw->smb_passwd == NULL) {
540 DEBUG(0,("change_lanman_password: no lanman password !\n"));
544 /* Get the new lanman hash. */
545 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
547 smbpw->smb_passwd = unenc_new_pw;
548 smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */
550 /* Now write it into the file. */
552 ret = mod_smbpwd_entry(smbpw,False);
558 /***********************************************************
559 Code to check and change the OEM hashed password.
560 ************************************************************/
561 BOOL pass_oem_change(char *user,
562 uchar *lmdata, uchar *lmhash,
563 uchar *ntdata, uchar *nthash)
566 struct smb_passwd *sampw;
567 BOOL ret = check_oem_password( user, lmdata, lmhash, ntdata, nthash,
569 new_passwd, sizeof(new_passwd));
572 * At this point we have the new case-sensitive plaintext
573 * password in the fstring new_passwd. If we wanted to synchronise
574 * with UNIX passwords we would call a UNIX password changing
575 * function here. However it would have to be done as root
576 * as the plaintext of the old users password is not
580 if ( ret && lp_unix_password_sync())
582 ret = chgpasswd(user,"", new_passwd, True);
587 ret = change_oem_password( sampw, new_passwd, False );
590 memset(new_passwd, 0, sizeof(new_passwd));
595 /***********************************************************
596 Code to check the OEM hashed password.
598 this function ignores the 516 byte nt OEM hashed password
599 but does use the lm OEM password to check the nt hashed-hash.
601 ************************************************************/
602 BOOL check_oem_password(char *user,
603 uchar *lmdata, uchar *lmhash,
604 uchar *ntdata, uchar *nthash,
605 struct smb_passwd **psmbpw, char *new_passwd,
608 static uchar null_pw[16];
609 static uchar null_ntpw[16];
610 struct smb_passwd *smbpw = NULL;
613 uchar unenc_old_ntpw[16];
615 uchar unenc_old_pw[16];
618 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
621 *psmbpw = smbpw = getsmbpwnam(user);
622 unbecome_root(False);
626 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
630 if (smbpw->acct_ctrl & ACB_DISABLED)
632 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
636 /* construct a null password (in case one is needed */
639 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
641 /* check for null passwords */
642 if (smbpw->smb_passwd == NULL)
644 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
646 smbpw->smb_passwd = null_pw;
650 DEBUG(0,("check_oem_password: no lanman password !\n"));
655 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
657 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
659 smbpw->smb_nt_passwd = null_pw;
663 DEBUG(0,("check_oem_password: no ntlm password !\n"));
669 * Call the hash function to get the new password.
671 SamOEMhash( (uchar *)lmdata, (uchar *)smbpw->smb_passwd, True);
674 * The length of the new password is in the last 4 bytes of
678 new_pw_len = IVAL(lmdata, 512);
679 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1)
681 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
688 * nt passwords are in unicode
690 int uni_pw_len = new_pw_len;
693 pw = unistrn2((uint16*)(&lmdata[512-uni_pw_len]), new_pw_len);
694 memcpy(new_passwd, pw, new_pw_len+1);
698 memcpy(new_passwd, &lmdata[512-new_pw_len], new_pw_len);
699 new_passwd[new_pw_len] = '\0';
703 * To ensure we got the correct new password, hash it and
704 * use it as a key to test the passed old password.
707 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
712 * Now use new_p16 as the key to see if the old
715 D_P16(new_p16 , lmhash, unenc_old_pw);
717 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
719 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
723 #ifdef DEBUG_PASSWORD
724 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
730 * Now use new_p16 as the key to see if the old
733 D_P16(new_ntp16, lmhash, unenc_old_pw);
734 D_P16(new_ntp16, nthash, unenc_old_ntpw);
736 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
738 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
742 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
744 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
747 #ifdef DEBUG_PASSWORD
748 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
753 /***********************************************************
754 Code to change the oem password. Changes both the lanman
756 override = False, normal
757 override = True, override XXXXXXXXXX'd password
758 ************************************************************/
760 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd, BOOL override)
763 fstring upper_case_new_passwd;
764 uchar new_nt_p16[16];
767 memset(upper_case_new_passwd, '\0', sizeof(upper_case_new_passwd));
768 fstrcpy(upper_case_new_passwd, new_passwd);
769 strupper(upper_case_new_passwd);
771 E_P16((uchar *)upper_case_new_passwd, new_p16);
773 smbpw->smb_passwd = new_p16;
775 E_md4hash((uchar *) new_passwd, new_nt_p16);
776 smbpw->smb_nt_passwd = new_nt_p16;
778 /* Now write it into the file. */
780 ret = mod_smbpwd_entry(smbpw,override);
783 memset(upper_case_new_passwd, '\0', strlen(upper_case_new_passwd));
784 memset(new_passwd, '\0', strlen(new_passwd));