cast ptsname to get rid of warning message
[Samba/ekacnet.git] / source / smbd / chgpasswd.c
blobdc16dad768efc6ba439de03a5dbc91043a2ca636
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
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.
27 /*
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
47 password.
50 #include "includes.h"
52 extern int DEBUGLEVEL;
54 #if ALLOW_CHANGE_PASSWORD
55 #define BUFSIZE 512
57 static int findpty(char **slave)
59 int master;
60 #ifndef HAVE_GRANTPT
61 static fstring line;
62 void *dirp;
63 char *dpname;
64 #endif /* !HAVE_GRANTPT */
66 #if defined(HAVE_GRANTPT)
67 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
68 grantpt(master);
69 unlockpt(master);
70 *slave = (char *)ptsname(master);
71 if (*slave == NULL) {
72 DEBUG(0,("findpty: Unable to create master/slave pty pair.\n"));
73 /* Stop fd leak on error. */
74 close(master);
75 return -1;
76 } else {
77 DEBUG(10, ("findpty: Allocated slave pty %s\n", *slave));
78 return (master);
81 #else /* HAVE_GRANTPT */
82 fstrcpy( line, "/dev/ptyXX" );
84 dirp = OpenDir(NULL, "/dev", False);
85 if (!dirp)
86 return(-1);
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 ) );
90 line[8] = dpname[3];
91 line[9] = dpname[4];
92 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
93 DEBUG(3,("pty: opened %s\n", line ) );
94 line[5] = 't';
95 *slave = line;
96 CloseDir(dirp);
97 return (master);
101 CloseDir(dirp);
102 #endif /* HAVE_GRANTPT */
103 return (-1);
106 static int dochild(int master,char *slavedev, char *name, char *passwordprogram, BOOL as_root)
108 int slave;
109 struct termios stermios;
110 struct passwd *pass = Get_Pwnam(name,True);
111 gid_t gid;
112 uid_t uid;
114 if (pass == NULL) {
115 DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n",
116 name));
117 return False;
120 gid = pass->pw_gid;
121 uid = pass->pw_uid;
123 gain_root_privilege();
125 /* Start new session - gets rid of controlling terminal. */
126 if (setsid() < 0) {
127 DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
128 return(False);
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",
134 slavedev));
135 return(False);
137 #ifdef I_PUSH
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"));
143 /* return(False); */
145 #endif
147 /* Close master. */
148 close(master);
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"));
154 return(False);
156 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
157 DEBUG(3,("Could not re-direct stdout\n"));
158 return(False);
160 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
161 DEBUG(3,("Could not re-direct stderr\n"));
162 return(False);
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"));
171 return(False);
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"));
178 return(False);
181 /* make us completely into the right uid */
182 if (!as_root) {
183 if(!become_user_permanently(uid, gid)) {
184 DEBUG(0,("dochild: unable to permanently become uid %d, gid %d\n", (int)uid, (int)gid));
185 return False;
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));
194 return(False);
196 return(True);
199 static int expect(int master,char *expected,char *buf)
201 int n, m;
203 n = 0;
204 buf[0] = 0;
205 while (1) {
206 if (n >= BUFSIZE-1) {
207 return False;
210 /* allow 4 seconds for some output to appear */
211 m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000);
212 if (m < 0)
213 return False;
215 n += m;
216 buf[n] = 0;
219 pstring s1,s2;
220 pstrcpy(s1,buf);
221 pstrcpy(s2,expected);
222 if (do_match(s1, s2, False))
223 return(True);
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)
238 int l;
240 l = strlen (s);
241 write (fd, s, l);
245 static int talktochild(int master, char *chatsequence)
247 char buf[BUFSIZE];
248 int count=0;
249 char *ptr=chatsequence;
250 fstring chatbuf;
252 *buf = 0;
253 sleep(1);
255 while (next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) {
256 BOOL ok=True;
257 count++;
258 pwd_sub(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));
265 if (!ok) {
266 DEBUG(3,("response %d incorrect\n",count));
267 return(False);
270 if (!next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) break;
271 pwd_sub(chatbuf);
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);
281 return (True);
285 static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root)
287 char *slavedev;
288 int master;
289 pid_t pid, wpid;
290 int wstat;
291 BOOL chstat = False;
293 /* allocate a pseudo-terminal device */
294 if ((master = findpty (&slavedev)) < 0) {
295 DEBUG(3,("Cannot Allocate pty for password change: %s\n",name));
296 return(False);
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));
308 close(master);
309 CatchChild();
310 return(False);
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) {
321 if(errno == EINTR) {
322 errno = 0;
323 continue;
325 break;
328 if (wpid < 0) {
329 DEBUG(3,("The process is no longer waiting!\n\n"));
330 close(master);
331 CatchChild();
332 return(False);
336 * Go back to ignoring children.
338 CatchChild();
340 close(master);
342 if (pid != wpid) {
343 DEBUG(3,("We were waiting for the wrong process ID\n"));
344 return(False);
346 if (WIFEXITED(wstat) == 0) {
347 DEBUG(3,("The process exited while we were waiting\n"));
348 return(False);
350 if (WEXITSTATUS(wstat) != 0) {
351 DEBUG(3,("The status of the process exiting was %d\n", wstat));
352 return(False);
355 } else {
356 /* CHILD */
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 */
365 alarm(20);
367 if (as_root)
368 become_root(False);
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 ));
377 exit(1);
380 if (chstat)
381 DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
382 return (chstat);
386 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
388 pstring passwordprogram;
389 pstring chatsequence;
390 size_t i;
391 size_t len;
393 strlower(name);
394 DEBUG(3,("Password change for user: %s\n",name));
396 #if DEBUG_PASSWORD
397 DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass));
398 #endif
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"));
421 return(False);
424 if (!*passwordprogram) {
425 DEBUG(2,("Null password program - no password changing\n"));
426 return(False);
430 * Check the old and new passwords don't contain any control
431 * characters.
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"));
438 return False;
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"));
446 return False;
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));
464 return(False);
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;
480 *psmbpw = NULL;
482 become_root(0);
483 smbpw = getsmbpwnam(user);
484 unbecome_root(0);
486 if (smbpw == NULL)
488 DEBUG(0,("check_lanman_password: getsmbpwnam returned NULL\n"));
489 return False;
492 if (smbpw->acct_ctrl & ACB_DISABLED)
494 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
495 return False;
498 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
500 uchar no_pw[14];
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"));
506 return False;
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"));
519 return False;
522 *psmbpw = smbpw;
523 return True;
526 /***********************************************************
527 Code to change the lanman hashed password.
528 It nulls out the NT hashed password as it will
529 no longer be valid.
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];
536 BOOL ret;
538 if (smbpw == NULL)
540 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
541 return False;
544 if (smbpw->acct_ctrl & ACB_DISABLED)
546 DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->smb_name));
547 return False;
550 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
552 uchar no_pw[14];
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"));
558 return False;
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. */
568 become_root(0);
569 ret = mod_smbpwd_entry(smbpw,False);
570 unbecome_root(0);
572 return ret;
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)
582 fstring new_passwd;
583 struct smb_passwd *sampw;
584 BOOL ret = check_oem_password( user, lmdata, lmhash, ntdata, nthash,
585 &sampw,
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
594 * available. JRA.
597 if ( ret && lp_unix_password_sync())
599 ret = chgpasswd(user,"", new_passwd, True);
602 if (ret)
604 ret = change_oem_password( sampw, new_passwd, False );
607 memset(new_passwd, 0, sizeof(new_passwd));
609 return ret;
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,
623 int new_passwd_size)
625 static uchar null_pw[16];
626 static uchar null_ntpw[16];
627 struct smb_passwd *smbpw = NULL;
628 int new_pw_len;
629 uchar new_ntp16[16];
630 uchar unenc_old_ntpw[16];
631 uchar new_p16[16];
632 uchar unenc_old_pw[16];
633 char no_pw[2];
635 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
637 become_root(False);
638 *psmbpw = smbpw = getsmbpwnam(user);
639 unbecome_root(False);
641 if (smbpw == NULL)
643 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
644 return False;
647 if (smbpw->acct_ctrl & ACB_DISABLED)
649 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
650 return False;
653 /* construct a null password (in case one is needed */
654 no_pw[0] = 0;
655 no_pw[1] = 0;
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;
665 else
667 DEBUG(0,("check_oem_password: no lanman password !\n"));
668 return False;
672 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
674 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
676 smbpw->smb_nt_passwd = null_pw;
678 else
680 DEBUG(0,("check_oem_password: no ntlm password !\n"));
681 return False;
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
692 * the data buffer.
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));
699 return False;
702 if (nt_pass_set)
705 * nt passwords are in unicode
707 int uni_pw_len = new_pw_len;
708 char *pw;
709 new_pw_len /= 2;
710 pw = unistrn2((uint16*)(&lmdata[512-uni_pw_len]), new_pw_len);
711 memcpy(new_passwd, pw, new_pw_len+1);
713 else
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);
726 if (!nt_pass_set)
729 * Now use new_p16 as the key to see if the old
730 * password matches.
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"));
737 return False;
740 #ifdef DEBUG_PASSWORD
741 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
742 #endif
743 return True;
747 * Now use new_p16 as the key to see if the old
748 * password matches.
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"));
756 return False;
759 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
761 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
762 return False;
764 #ifdef DEBUG_PASSWORD
765 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
766 #endif
767 return True;
770 /***********************************************************
771 Code to change the oem password. Changes both the lanman
772 and NT hashes.
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)
779 int ret;
780 uchar new_nt_p16[16];
781 uchar new_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. */
789 become_root(0);
790 ret = mod_smbpwd_entry(smbpw,override);
791 unbecome_root(0);
793 memset(new_passwd, '\0', strlen(new_passwd));
795 return ret;