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
57 static int findpty(char **slave
)
64 #endif /* !HAVE_GRANTPT */
66 #if defined(HAVE_GRANTPT)
67 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 0) {
70 *slave
= (char *)ptsname(master
);
72 DEBUG(0,("findpty: Unable to create master/slave pty pair.\n"));
73 /* Stop fd leak on error. */
77 DEBUG(10, ("findpty: Allocated slave pty %s\n", *slave
));
81 #else /* HAVE_GRANTPT */
82 fstrcpy( line
, "/dev/ptyXX" );
84 dirp
= OpenDir(NULL
, "/dev", False
);
87 while ((dpname
= ReadDirName(dirp
)) != NULL
) {
88 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5) {
89 DEBUG(3,("pty: try to open %s, line was %s\n", dpname
, line
) );
92 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0) {
93 DEBUG(3,("pty: opened %s\n", line
) );
102 #endif /* HAVE_GRANTPT */
106 static int dochild(int master
,char *slavedev
, char *name
, char *passwordprogram
, BOOL as_root
)
109 struct termios stermios
;
110 struct passwd
*pass
= Get_Pwnam(name
,True
);
115 DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n",
123 gain_root_privilege();
125 /* Start new session - gets rid of controlling terminal. */
127 DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
131 /* Open slave pty and acquire as new controlling terminal. */
132 if ((slave
= sys_open(slavedev
, O_RDWR
, 0)) < 0) {
133 DEBUG(3,("More weirdness, could not open %s\n",
138 ioctl(slave
, I_PUSH
, "ptem");
139 ioctl(slave
, I_PUSH
, "ldterm");
140 #elif defined(TIOCSCTTY)
141 if (ioctl(slave
,TIOCSCTTY
,0) <0) {
142 DEBUG(3,("Error in ioctl call for slave pty\n"));
150 /* Make slave stdin/out/err of child. */
152 if (dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
) {
153 DEBUG(3,("Could not re-direct stdin\n"));
156 if (dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
) {
157 DEBUG(3,("Could not re-direct stdout\n"));
160 if (dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
) {
161 DEBUG(3,("Could not re-direct stderr\n"));
164 if (slave
> 2) close(slave
);
166 /* Set proper terminal attributes - no echo, canonical input processing,
167 no map NL to CR/NL on output. */
169 if (tcgetattr(0, &stermios
) < 0) {
170 DEBUG(3,("could not read default terminal attributes on pty\n"));
173 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
174 stermios
.c_lflag
|= ICANON
;
175 stermios
.c_oflag
&= ~(ONLCR
);
176 if (tcsetattr(0, TCSANOW
, &stermios
) < 0) {
177 DEBUG(3,("could not set attributes of pty\n"));
181 /* make us completely into the right uid */
183 if(!become_user_permanently(uid
, gid
)) {
184 DEBUG(0,("dochild: unable to permanently become uid %d, gid %d\n", (int)uid
, (int)gid
));
189 DEBUG(10, ("Invoking '%s' as password change program.\n", passwordprogram
));
191 /* execl() password-change application */
192 if (execl("/bin/sh","sh","-c",passwordprogram
,NULL
) < 0) {
193 DEBUG(3,("Bad status returned from %s\n",passwordprogram
));
199 static int expect(int master
,char *expected
,char *buf
)
206 if (n
>= BUFSIZE
-1) {
210 /* allow 4 seconds for some output to appear */
211 m
= read_with_timeout(master
, buf
+n
, 1, BUFSIZE
-1-n
, 4000);
221 pstrcpy(s2
,expected
);
222 if (do_match(s1
, s2
, False
))
228 static void pwd_sub(char *buf
)
230 string_sub(buf
,"\\n","\n");
231 string_sub(buf
,"\\r","\r");
232 string_sub(buf
,"\\s"," ");
233 string_sub(buf
,"\\t","\t");
236 static void writestring(int fd
,char *s
)
245 static int talktochild(int master
, char *chatsequence
)
249 char *ptr
=chatsequence
;
255 while (next_token(&ptr
,chatbuf
,NULL
,sizeof(chatbuf
))) {
259 if (!strequal(chatbuf
,"."))
260 ok
= expect(master
,chatbuf
,buf
);
262 if (lp_passwd_chat_debug())
263 DEBUG(100,("talktochild: chatbuf=[%s] responsebuf=[%s]\n",chatbuf
,buf
));
266 DEBUG(3,("response %d incorrect\n",count
));
270 if (!next_token(&ptr
,chatbuf
,NULL
,sizeof(chatbuf
))) break;
272 if (!strequal(chatbuf
,"."))
273 writestring(master
,chatbuf
);
275 if (lp_passwd_chat_debug())
276 DEBUG(100,("talktochild: sendbuf=[%s]\n",chatbuf
));
279 if (count
<1) return(False
);
285 static BOOL
chat_with_program(char *passwordprogram
,char *name
,char *chatsequence
, BOOL as_root
)
293 /* allocate a pseudo-terminal device */
294 if ((master
= findpty (&slavedev
)) < 0) {
295 DEBUG(3,("Cannot Allocate pty for password change: %s\n",name
));
300 * We need to temporarily stop CatchChild from eating
301 * SIGCLD signals as it also eats the exit status code. JRA.
304 CatchChildLeaveStatus();
306 if ((pid
= fork()) < 0) {
307 DEBUG(3,("Cannot fork() child for password change: %s\n",name
));
313 /* we now have a pty */
314 if (pid
> 0){ /* This is the parent process */
315 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
316 DEBUG(3,("Child failed to change password: %s\n",name
));
317 kill(pid
, SIGKILL
); /* be sure to end this process */
320 while((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
329 DEBUG(3,("The process is no longer waiting!\n\n"));
336 * Go back to ignoring children.
343 DEBUG(3,("We were waiting for the wrong process ID\n"));
346 if (WIFEXITED(wstat
) == 0) {
347 DEBUG(3,("The process exited while we were waiting\n"));
350 if (WEXITSTATUS(wstat
) != 0) {
351 DEBUG(3,("The status of the process exiting was %d\n", wstat
));
359 * Lose any oplock capabilities.
361 set_process_capability(KERNEL_OPLOCK_CAPABILITY
, False
);
362 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY
, False
);
364 /* make sure it doesn't freeze */
369 DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name
,(int)getuid(),(int)getgid()));
370 chstat
= dochild(master
, slavedev
, name
, passwordprogram
, as_root
);
373 * The child should never return from dochild() ....
376 DEBUG(0,("chat_with_program: Error: dochild() returned %d\n", chstat
));
381 DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat
?"":"un"), name
));
386 BOOL
chgpasswd(char *name
,char *oldpass
,char *newpass
, BOOL as_root
)
388 pstring passwordprogram
;
389 pstring chatsequence
;
394 DEBUG(3,("Password change for user: %s\n",name
));
397 DEBUG(100,("Passwords: old=%s new=%s\n",oldpass
,newpass
));
400 /* Take the passed information and test it for minimum criteria */
401 /* Minimum password length */
402 if (strlen(newpass
) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */
404 DEBUG(0,("Password Change: user %s, New password is shorter than minimum password length = %d\n",
405 name
, lp_min_passwd_length()));
406 return (False
); /* inform the user */
409 /* Password is same as old password */
410 if (strcmp(oldpass
,newpass
) == 0) /* don't allow same password */
412 DEBUG(2,("Password Change: %s, New password is same as old\n",name
)); /* log the attempt */
413 return (False
); /* inform the user */
416 pstrcpy(passwordprogram
,lp_passwd_program());
417 pstrcpy(chatsequence
,lp_passwd_chat());
419 if (!*chatsequence
) {
420 DEBUG(2,("Null chat sequence - no password changing\n"));
424 if (!*passwordprogram
) {
425 DEBUG(2,("Null password program - no password changing\n"));
430 * Check the old and new passwords don't contain any control
434 len
= strlen(oldpass
);
435 for(i
= 0; i
< len
; i
++) {
436 if (iscntrl((int)oldpass
[i
])) {
437 DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n"));
442 len
= strlen(newpass
);
443 for(i
= 0; i
< len
; i
++) {
444 if (iscntrl((int)newpass
[i
])) {
445 DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n"));
450 string_sub(passwordprogram
,"%u",name
);
451 all_string_sub(passwordprogram
,"%o",oldpass
);
452 all_string_sub(passwordprogram
,"%n",newpass
);
454 string_sub(chatsequence
,"%u",name
);
455 all_string_sub(chatsequence
,"%o",oldpass
);
456 all_string_sub(chatsequence
,"%n",newpass
);
457 return(chat_with_program(passwordprogram
,name
,chatsequence
, as_root
));
460 #else /* ALLOW_CHANGE_PASSWORD */
461 BOOL
chgpasswd(char *name
,char *oldpass
,char *newpass
, BOOL as_root
)
463 DEBUG(0,("Password changing not compiled in (user=%s)\n",name
));
466 #endif /* ALLOW_CHANGE_PASSWORD */
468 /***********************************************************
469 Code to check the lanman hashed password.
470 ************************************************************/
472 BOOL
check_lanman_password(char *user
, uchar
*pass1
,
473 uchar
*pass2
, struct smb_passwd
**psmbpw
)
475 static uchar null_pw
[16];
476 uchar unenc_new_pw
[16];
477 uchar unenc_old_pw
[16];
478 struct smb_passwd
*smbpw
;
483 smbpw
= getsmbpwnam(user
);
488 DEBUG(0,("check_lanman_password: getsmbpwnam returned NULL\n"));
492 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
494 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
498 if ((smbpw
->smb_passwd
== NULL
) && (smbpw
->acct_ctrl
& ACB_PWNOTREQ
))
501 memset(no_pw
, '\0', 14);
502 E_P16(no_pw
, null_pw
);
503 smbpw
->smb_passwd
= null_pw
;
504 } else if (smbpw
->smb_passwd
== NULL
) {
505 DEBUG(0,("check_lanman_password: no lanman password !\n"));
509 /* Get the new lanman hash. */
510 D_P16(smbpw
->smb_passwd
, pass2
, unenc_new_pw
);
512 /* Use this to get the old lanman hash. */
513 D_P16(unenc_new_pw
, pass1
, unenc_old_pw
);
515 /* Check that the two old passwords match. */
516 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
518 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
526 /***********************************************************
527 Code to change the lanman hashed password.
528 It nulls out the NT hashed password as it will
530 ************************************************************/
532 BOOL
change_lanman_password(struct smb_passwd
*smbpw
, uchar
*pass1
, uchar
*pass2
)
534 static uchar null_pw
[16];
535 uchar unenc_new_pw
[16];
540 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
544 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
546 DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw
->smb_name
));
550 if ((smbpw
->smb_passwd
== NULL
) && (smbpw
->acct_ctrl
& ACB_PWNOTREQ
))
553 memset(no_pw
, '\0', 14);
554 E_P16(no_pw
, null_pw
);
555 smbpw
->smb_passwd
= null_pw
;
556 } else if (smbpw
->smb_passwd
== NULL
) {
557 DEBUG(0,("change_lanman_password: no lanman password !\n"));
561 /* Get the new lanman hash. */
562 D_P16(smbpw
->smb_passwd
, pass2
, unenc_new_pw
);
564 smbpw
->smb_passwd
= unenc_new_pw
;
565 smbpw
->smb_nt_passwd
= NULL
; /* We lose the NT hash. Sorry. */
567 /* Now write it into the file. */
569 ret
= mod_smbpwd_entry(smbpw
,False
);
575 /***********************************************************
576 Code to check and change the OEM hashed password.
577 ************************************************************/
578 BOOL
pass_oem_change(char *user
,
579 uchar
*lmdata
, uchar
*lmhash
,
580 uchar
*ntdata
, uchar
*nthash
)
583 struct smb_passwd
*sampw
;
584 BOOL ret
= check_oem_password( user
, lmdata
, lmhash
, ntdata
, nthash
,
586 new_passwd
, sizeof(new_passwd
));
589 * At this point we have the new case-sensitive plaintext
590 * password in the fstring new_passwd. If we wanted to synchronise
591 * with UNIX passwords we would call a UNIX password changing
592 * function here. However it would have to be done as root
593 * as the plaintext of the old users password is not
597 if ( ret
&& lp_unix_password_sync())
599 ret
= chgpasswd(user
,"", new_passwd
, True
);
604 ret
= change_oem_password( sampw
, new_passwd
, False
);
607 memset(new_passwd
, 0, sizeof(new_passwd
));
612 /***********************************************************
613 Code to check the OEM hashed password.
615 this function ignores the 516 byte nt OEM hashed password
616 but does use the lm OEM password to check the nt hashed-hash.
618 ************************************************************/
619 BOOL
check_oem_password(char *user
,
620 uchar
*lmdata
, uchar
*lmhash
,
621 uchar
*ntdata
, uchar
*nthash
,
622 struct smb_passwd
**psmbpw
, char *new_passwd
,
625 static uchar null_pw
[16];
626 static uchar null_ntpw
[16];
627 struct smb_passwd
*smbpw
= NULL
;
630 uchar unenc_old_ntpw
[16];
632 uchar unenc_old_pw
[16];
635 BOOL nt_pass_set
= (ntdata
!= NULL
&& nthash
!= NULL
);
638 *psmbpw
= smbpw
= getsmbpwnam(user
);
639 unbecome_root(False
);
643 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
647 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
649 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
653 /* construct a null password (in case one is needed */
656 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
658 /* check for null passwords */
659 if (smbpw
->smb_passwd
== NULL
)
661 if (smbpw
->acct_ctrl
& ACB_PWNOTREQ
)
663 smbpw
->smb_passwd
= null_pw
;
667 DEBUG(0,("check_oem_password: no lanman password !\n"));
672 if (smbpw
->smb_nt_passwd
== NULL
&& nt_pass_set
)
674 if (smbpw
->acct_ctrl
& ACB_PWNOTREQ
)
676 smbpw
->smb_nt_passwd
= null_pw
;
680 DEBUG(0,("check_oem_password: no ntlm password !\n"));
686 * Call the hash function to get the new password.
688 SamOEMhash( (uchar
*)lmdata
, (uchar
*)smbpw
->smb_passwd
, True
);
691 * The length of the new password is in the last 4 bytes of
695 new_pw_len
= IVAL(lmdata
, 512);
696 if (new_pw_len
< 0 || new_pw_len
> new_passwd_size
- 1)
698 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len
));
705 * nt passwords are in unicode
707 int uni_pw_len
= new_pw_len
;
710 pw
= unistrn2((uint16
*)(&lmdata
[512-uni_pw_len
]), new_pw_len
);
711 memcpy(new_passwd
, pw
, new_pw_len
+1);
715 memcpy(new_passwd
, &lmdata
[512-new_pw_len
], new_pw_len
);
716 new_passwd
[new_pw_len
] = '\0';
720 * To ensure we got the correct new password, hash it and
721 * use it as a key to test the passed old password.
724 nt_lm_owf_gen(new_passwd
, new_ntp16
, new_p16
);
729 * Now use new_p16 as the key to see if the old
732 D_P16(new_p16
, lmhash
, unenc_old_pw
);
734 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
736 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
740 #ifdef DEBUG_PASSWORD
741 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd
));
747 * Now use new_p16 as the key to see if the old
750 D_P16(new_ntp16
, lmhash
, unenc_old_pw
);
751 D_P16(new_ntp16
, nthash
, unenc_old_ntpw
);
753 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
755 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
759 if (memcmp(smbpw
->smb_nt_passwd
, unenc_old_ntpw
, 16))
761 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
764 #ifdef DEBUG_PASSWORD
765 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd
));
770 /***********************************************************
771 Code to change the oem password. Changes both the lanman
773 override = False, normal
774 override = True, override XXXXXXXXXX'd password
775 ************************************************************/
777 BOOL
change_oem_password(struct smb_passwd
*smbpw
, char *new_passwd
, BOOL override
)
780 uchar new_nt_p16
[16];
783 nt_lm_owf_gen(new_passwd
, new_nt_p16
, new_p16
);
785 smbpw
->smb_passwd
= new_p16
;
786 smbpw
->smb_nt_passwd
= new_nt_p16
;
788 /* Now write it into the file. */
790 ret
= mod_smbpwd_entry(smbpw
,override
);
793 memset(new_passwd
, '\0', strlen(new_passwd
));