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
)
64 struct dirent
*dentry
;
66 #endif /* !HAVE_GRANTPT */
68 #if defined(HAVE_GRANTPT)
69 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 1) {
72 *slave
= ptsname(master
);
74 DEBUG(0,("findpty: Unable to create master/slave pty pair.\n"));
75 /* Stop fd leak on error. */
79 DEBUG(10, ("findpty: Allocated slave pty %s\n", *slave
));
83 #else /* HAVE_GRANTPT */
84 fstrcpy( line
, "/dev/ptyXX" );
86 dirp
= opendir("/dev");
89 while ((dentry
= readdir(dirp
)) != NULL
) {
90 dpname
= dentry
->d_name
;
91 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5) {
92 DEBUG(3,("pty: try to open %s, line was %s\n", dpname
, line
) );
95 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0) {
96 DEBUG(3,("pty: opened %s\n", line
) );
105 #endif /* HAVE_GRANTPT */
109 static int dochild(int master
,char *slavedev
, const char *_name
, char *passwordprogram
, BOOL as_root
)
112 struct termios stermios
;
113 const struct passwd
*pass
;
118 fstrcpy(name
, _name
);
119 pass
= Get_Pwnam(name
,True
);
122 DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n",
129 #ifdef HAVE_SETRESUID
135 /* Start new session - gets rid of controlling terminal. */
137 DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
141 /* Open slave pty and acquire as new controlling terminal. */
142 if ((slave
= sys_open(slavedev
, O_RDWR
, 0)) < 0) {
143 DEBUG(3,("More weirdness, could not open %s\n",
148 ioctl(slave
, I_PUSH
, "ptem");
149 ioctl(slave
, I_PUSH
, "ldterm");
150 #elif defined(TIOCSCTTY)
151 if (ioctl(slave
,TIOCSCTTY
,0) <0) {
152 DEBUG(3,("Error in ioctl call for slave pty\n"));
160 /* Make slave stdin/out/err of child. */
162 if (dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
) {
163 DEBUG(3,("Could not re-direct stdin\n"));
166 if (dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
) {
167 DEBUG(3,("Could not re-direct stdout\n"));
170 if (dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
) {
171 DEBUG(3,("Could not re-direct stderr\n"));
174 if (slave
> 2) close(slave
);
176 /* Set proper terminal attributes - no echo, canonical input processing,
177 no map NL to CR/NL on output. */
179 if (tcgetattr(0, &stermios
) < 0) {
180 DEBUG(3,("could not read default terminal attributes on pty\n"));
183 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
184 stermios
.c_lflag
|= ICANON
;
185 stermios
.c_oflag
&= ~(ONLCR
);
186 if (tcsetattr(0, TCSANOW
, &stermios
) < 0) {
187 DEBUG(3,("could not set attributes of pty\n"));
191 /* make us completely into the right uid */
193 #ifdef HAVE_SETRESUID
196 setresgid(gid
,gid
,gid
);
197 setresuid(uid
,uid
,uid
);
208 DEBUG(10, ("Invoking '%s' as password change program.\n", passwordprogram
));
210 /* execl() password-change application */
211 if (execl("/bin/sh","sh","-c",passwordprogram
,NULL
) < 0) {
212 DEBUG(3,("Bad status returned from %s\n",passwordprogram
));
218 static int expect(int master
,char *expected
,char *buf
)
225 if (n
>= BUFSIZE
-1) {
229 /* allow 4 seconds for some output to appear */
230 m
= read_with_timeout(master
, buf
+n
, 1, BUFSIZE
-1-n
, 4000);
240 pstrcpy(s2
,expected
);
241 if (do_match(s1
, s2
, False
))
247 static void pwd_sub(char *buf
)
249 string_sub(buf
,"\\n","\n");
250 string_sub(buf
,"\\r","\r");
251 string_sub(buf
,"\\s"," ");
252 string_sub(buf
,"\\t","\t");
255 static void writestring(int fd
,char *s
)
264 static int talktochild(int master
, char *chatsequence
)
268 char *ptr
=chatsequence
;
274 while (next_token(&ptr
,chatbuf
,NULL
,sizeof(chatbuf
))) {
278 if (!strequal(chatbuf
,"."))
279 ok
= expect(master
,chatbuf
,buf
);
281 if (lp_passwd_chat_debug())
282 DEBUG(100,("talktochild: chatbuf=[%s] responsebuf=[%s]\n",chatbuf
,buf
));
285 DEBUG(3,("response %d incorrect\n",count
));
289 if (!next_token(&ptr
,chatbuf
,NULL
,sizeof(chatbuf
))) break;
291 if (!strequal(chatbuf
,"."))
292 writestring(master
,chatbuf
);
294 if (lp_passwd_chat_debug())
295 DEBUG(100,("talktochild: sendbuf=[%s]\n",chatbuf
));
298 if (count
<1) return(False
);
304 static BOOL
chat_with_program(char *passwordprogram
,const char *name
,char *chatsequence
, BOOL as_root
)
312 /* allocate a pseudo-terminal device */
313 if ((master
= findpty (&slavedev
)) < 0) {
314 DEBUG(3,("Cannot Allocate pty for password change: %s\n",name
));
318 if ((pid
= fork()) < 0) {
319 DEBUG(3,("Cannot fork() child for password change: %s\n",name
));
324 /* we now have a pty */
325 if (pid
> 0){ /* This is the parent process */
326 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
327 DEBUG(3,("Child failed to change password: %s\n",name
));
328 kill(pid
, SIGKILL
); /* be sure to end this process */
331 if ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
332 DEBUG(3,("The process is no longer waiting!\n\n"));
340 DEBUG(3,("We were waiting for the wrong process ID\n"));
343 if (WIFEXITED(wstat
) == 0) {
344 DEBUG(3,("The process exited while we were waiting\n"));
347 if (WEXITSTATUS(wstat
) != 0) {
348 DEBUG(3,("The status of the process exiting was %d\n", wstat
));
356 * Lose any oplock capabilities.
358 set_process_capability(KERNEL_OPLOCK_CAPABILITY
, False
);
359 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY
, False
);
361 /* make sure it doesn't freeze */
366 DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name
,(int)getuid(),(int)getgid()));
367 chstat
= dochild(master
, slavedev
, name
, passwordprogram
, as_root
);
370 unbecome_root(False
);
374 DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat
?"":"un"), name
));
379 BOOL
chgpasswd(const char *_name
,char *oldpass
,char *newpass
, BOOL as_root
)
381 pstring passwordprogram
;
382 pstring chatsequence
;
387 fstrcpy(name
, _name
);
389 DEBUG(3,("Password change for user: %s\n",name
));
392 DEBUG(100,("Passwords: old=%s new=%s\n",oldpass
,newpass
));
395 /* Take the passed information and test it for minimum criteria */
396 /* Minimum password length */
397 if (strlen(newpass
) < MINPASSWDLENGTH
) /* too short, must be at least MINPASSWDLENGTH */
399 DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name
));
400 return (False
); /* inform the user */
403 /* Password is same as old password */
404 if (strcmp(oldpass
,newpass
) == 0) /* don't allow same password */
406 DEBUG(2,("Password Change: %s, New password is same as old\n",name
)); /* log the attempt */
407 return (False
); /* inform the user */
410 pstrcpy(passwordprogram
,lp_passwd_program());
411 pstrcpy(chatsequence
,lp_passwd_chat());
413 if (!*chatsequence
) {
414 DEBUG(2,("Null chat sequence - no password changing\n"));
418 if (!*passwordprogram
) {
419 DEBUG(2,("Null password program - no password changing\n"));
424 * Check the old and new passwords don't contain any control
428 len
= strlen(oldpass
);
429 for(i
= 0; i
< len
; i
++) {
430 if (iscntrl((int)oldpass
[i
])) {
431 DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n"));
436 len
= strlen(newpass
);
437 for(i
= 0; i
< len
; i
++) {
438 if (iscntrl((int)newpass
[i
])) {
439 DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n"));
444 string_sub(passwordprogram
,"%u",name
);
445 all_string_sub(passwordprogram
,"%o",oldpass
, 0);
446 all_string_sub(passwordprogram
,"%n",newpass
, 0);
448 string_sub(chatsequence
,"%u",name
);
449 all_string_sub(chatsequence
,"%o",oldpass
, sizeof(pstring
));
450 all_string_sub(chatsequence
,"%n",newpass
, sizeof(pstring
));
451 return(chat_with_program(passwordprogram
,name
,chatsequence
, as_root
));
454 #else /* ALLOW_CHANGE_PASSWORD */
455 BOOL
chgpasswd(const char *name
,char *oldpass
,char *newpass
, BOOL as_root
)
457 DEBUG(0,("Password changing not compiled in (user=%s)\n",name
));
460 #endif /* ALLOW_CHANGE_PASSWORD */
462 /***********************************************************
463 Code to check the OEM hashed password.
465 this function ignores the 516 byte nt OEM hashed password
466 but does use the lm OEM password to check the nt hashed-hash.
468 ************************************************************/
469 static BOOL
check_oem_password(const char *user
,
470 const uchar
*_lmdata
, const uchar
*lmhash
,
471 const uchar
*_ntdata
, const uchar
*nthash
,
472 struct smb_passwd
**psmbpw
, UNISTR2
*new_passwd
)
474 static uchar null_pw
[16];
475 static uchar null_ntpw
[16];
476 struct smb_passwd
*smbpw
= NULL
;
478 uchar unenc_old_ntpw
[16];
480 uchar unenc_old_pw
[16];
486 BOOL nt_pass_set
= (_ntdata
!= NULL
&& nthash
!= NULL
);
490 memcpy(lmdata
, _lmdata
, sizeof(lmdata
));
495 memcpy(ntdata
, _ntdata
, sizeof(ntdata
));
499 (*psmbpw
) = smbpw
= getsmbpwnam(user
);
500 unbecome_root(False
);
504 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
508 if (IS_BITS_SET_ALL(smbpw
->acct_ctrl
, ACB_DISABLED
))
510 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
514 /* construct a null password (in case one is needed */
517 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
519 /* check for null passwords */
520 if (smbpw
->smb_passwd
== NULL
)
522 if (smbpw
->acct_ctrl
& ACB_PWNOTREQ
)
524 smbpw
->smb_passwd
= null_pw
;
528 DEBUG(0,("check_oem_password: no lanman password !\n"));
533 if (smbpw
->smb_nt_passwd
== NULL
&& nt_pass_set
)
535 if (smbpw
->acct_ctrl
& ACB_PWNOTREQ
)
537 smbpw
->smb_nt_passwd
= null_pw
;
541 DEBUG(0,("check_oem_password: no ntlm password !\n"));
547 * Call the hash function to get the new password.
549 SamOEMhash( (uchar
*)lmdata
, (uchar
*)smbpw
->smb_passwd
, True
);
551 if (!decode_pw_buffer(lmdata
, (char*)new_passwd
->buffer
, 256, &len
))
557 * To ensure we got the correct new password, hash it and
558 * use it as a key to test the passed old password.
563 DEBUG(10,("check_oem_password: non-unicode\n"));
564 nt_lm_owf_gen((char*)new_passwd
->buffer
, new_ntp16
, new_p16
);
567 * Now use new_p16 as the key to see if the old
570 D_P16(new_p16
, lmhash
, unenc_old_pw
);
572 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
574 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
578 #ifdef DEBUG_PASSWORD
579 DEBUG(100,("check_oem_password: password %s ok\n",
580 (char*)new_passwd
->buffer
));
586 new_passwd
->uni_max_len
= len
/ 2;
587 new_passwd
->uni_str_len
= len
/ 2;
588 nt_lm_owf_genW(new_passwd
, new_ntp16
, new_p16
);
592 * Now use new_p16 as the key to see if the old
595 D_P16(new_ntp16
, lmhash
, unenc_old_pw
);
596 D_P16(new_ntp16
, nthash
, unenc_old_ntpw
);
598 #ifdef DEBUG_PASSWORD
599 dump_data(100, lmhash
, 16);
600 dump_data(100, unenc_old_pw
, 16);
601 dump_data(100, new_ntp16
, 16);
602 dump_data(100, smbpw
->smb_passwd
, 16);
604 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
606 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
610 if (memcmp(smbpw
->smb_nt_passwd
, unenc_old_ntpw
, 16))
612 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
615 #ifdef DEBUG_PASSWORD
616 DEBUG(100,("check_oem_password: password ok\n"));
621 /***********************************************************
622 Code to check and change the OEM hashed password.
623 ************************************************************/
624 BOOL
pass_oem_change(const char *user
,
625 const uchar
*lmdata
, const uchar
*lmhash
,
626 const uchar
*ntdata
, const uchar
*nthash
)
629 struct smb_passwd
*sampw
= NULL
;
630 BOOL ret
= check_oem_password( user
, lmdata
, lmhash
, ntdata
, nthash
,
634 /* now we check to see if we are actually allowed to change the
637 if (ret
&& (sampw
== NULL
||
638 IS_BITS_SET_ALL(sampw
->acct_ctrl
,ACB_PWLOCK
)))
642 DEBUG(3,("pass_oem_change: account %s not known\n",
647 DEBUG(3,("pass_oem_change: account %s disabled (%x)\n",
648 user
, sampw
->acct_ctrl
));
654 * At this point we have the new case-sensitive plaintext
655 * password in the fstring new_passwd. If we wanted to synchronise
656 * with UNIX passwords we would call a UNIX password changing
657 * function here. However it would have to be done as root
658 * as the plaintext of the old users password is not
662 if ( ret
&& lp_unix_password_sync())
664 ret
= chgpasswd(user
,"", (char*)new_passwd
.buffer
, True
);
669 ret
= change_oem_password( sampw
, &new_passwd
,
670 ntdata
!= NULL
, False
);
673 ZERO_STRUCT(new_passwd
);
678 /***********************************************************
679 Code to change the oem password. Changes both the lanman
681 override = False, normal
682 override = True, override XXXXXXXXXX'd password
683 ************************************************************/
685 BOOL
change_oem_password(struct smb_passwd
*smbpw
, UNISTR2
*new_passwd
,
686 BOOL unicode
, BOOL override
)
689 uchar new_nt_p16
[16];
692 DEBUG(100,("change_oem_password: %d\n", __LINE__
));
696 nt_lm_owf_genW(new_passwd
, new_nt_p16
, new_p16
);
700 nt_lm_owf_gen((char*)new_passwd
->buffer
, new_nt_p16
, new_p16
);
703 DEBUG(100,("change_oem_password: %d\n", __LINE__
));
706 smbpw
->smb_passwd
= new_p16
;
707 smbpw
->smb_nt_passwd
= new_nt_p16
;
709 DEBUG(100,("change_oem_password: %d\n", __LINE__
));
712 /* Now write it into the file. */
714 ret
= mod_smbpwd_entry(smbpw
,override
);
717 ZERO_STRUCTP(new_passwd
);
722 /****************************************************************************
723 update the encrypted smbpasswd file from the plaintext username and password
724 *****************************************************************************/
725 BOOL
update_smbpassword_file(const char *user
, const char *password
)
727 struct smb_passwd
*smbpw
;
732 smbpw
= getsmbpwnam(user
);
737 DEBUG(0,("getsmbpwnam returned NULL\n"));
741 make_unistr2(&newpw
, password
, password
!= NULL
? strlen(password
) : 0);
743 /* Here, the flag is one, because we want to ignore the
744 XXXXXXX'd out password */
745 ret
= change_oem_password( smbpw
, &newpw
, True
, True
);
748 DEBUG(3,("change_oem_password returned False\n"));