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
, unsigned char *pass1
,
456 unsigned char *pass2
, struct smb_passwd
**psmbpw
)
458 unsigned char unenc_new_pw
[16];
459 unsigned char unenc_old_pw
[16];
460 unsigned char null_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
))
483 unsigned char no_pw
[14];
484 memset(no_pw
, '\0', 14);
485 E_P16((uchar
*)no_pw
, (uchar
*)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
, unsigned char *pass1
, unsigned char *pass2
)
517 unsigned char unenc_new_pw
[16];
518 unsigned char null_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
))
535 unsigned char no_pw
[14];
536 memset(no_pw
, '\0', 14);
537 E_P16((uchar
*)no_pw
, (uchar
*)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 the OEM hashed password.
560 ************************************************************/
562 BOOL
check_oem_password(char *user
, unsigned char *data
,
563 struct smb_passwd
**psmbpw
, char *new_passwd
,
566 struct smb_passwd
*smbpw
= NULL
;
568 fstring upper_case_new_passwd
;
569 unsigned char new_p16
[16];
570 unsigned char unenc_old_pw
[16];
571 unsigned char null_pw
[16];
574 *psmbpw
= smbpw
= getsmbpwnam(user
);
579 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
583 if(smbpw
->acct_ctrl
& ACB_DISABLED
)
585 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
589 if((smbpw
->smb_passwd
== NULL
) && (smbpw
->acct_ctrl
& ACB_PWNOTREQ
))
591 unsigned char no_pw
[14];
592 memset(no_pw
, '\0', 14);
593 E_P16((uchar
*)no_pw
, (uchar
*)null_pw
);
594 smbpw
->smb_passwd
= null_pw
;
595 } else if (smbpw
->smb_passwd
== NULL
) {
596 DEBUG(0,("check_oem_password: no lanman password !\n"));
601 * Call the hash function to get the new password.
603 SamOEMhash( (unsigned char *)data
, (unsigned char *)smbpw
->smb_passwd
, True
);
606 * The length of the new password is in the last 4 bytes of
609 new_pw_len
= IVAL(data
,512);
610 if(new_pw_len
< 0 || new_pw_len
> new_passwd_size
- 1) {
611 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len
));
615 memcpy(new_passwd
, &data
[512-new_pw_len
], new_pw_len
);
616 new_passwd
[new_pw_len
] = '\0';
619 * To ensure we got the correct new password, hash it and
620 * use it as a key to test the passed old password.
623 memset(upper_case_new_passwd
, '\0', sizeof(upper_case_new_passwd
));
624 fstrcpy(upper_case_new_passwd
, new_passwd
);
625 strupper(upper_case_new_passwd
);
627 E_P16((uchar
*)upper_case_new_passwd
, new_p16
);
630 * Now use new_p16 as the key to see if the old
633 D_P16(new_p16
, &data
[516], unenc_old_pw
);
635 if(memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16)) {
636 DEBUG(0,("check_oem_password: old password doesn't match.\n"));
640 memset(upper_case_new_passwd
, '\0', strlen(upper_case_new_passwd
));
645 /***********************************************************
646 Code to change the oem password. Changes both the lanman
648 override = False, normal
649 override = True, override XXXXXXXXXX'd password
650 ************************************************************/
652 BOOL
change_oem_password(struct smb_passwd
*smbpw
, char *new_passwd
, BOOL override
)
655 fstring upper_case_new_passwd
;
656 unsigned char new_nt_p16
[16];
657 unsigned char new_p16
[16];
659 memset(upper_case_new_passwd
, '\0', sizeof(upper_case_new_passwd
));
660 fstrcpy(upper_case_new_passwd
, new_passwd
);
661 strupper(upper_case_new_passwd
);
663 E_P16((uchar
*)upper_case_new_passwd
, new_p16
);
665 smbpw
->smb_passwd
= new_p16
;
667 E_md4hash((uchar
*) new_passwd
, new_nt_p16
);
668 smbpw
->smb_nt_passwd
= new_nt_p16
;
670 /* Now write it into the file. */
672 ret
= mod_smbpwd_entry(smbpw
,override
);
675 memset(upper_case_new_passwd
, '\0', strlen(upper_case_new_passwd
));
676 memset(new_passwd
, '\0', strlen(new_passwd
));