- removed smb.conf.5.html as it now comes as part of htmldocs
[Samba/gbeck.git] / source / smbd / chgpasswd.c
blob30b9b3fed60aac3ddc2f67748295436ca502e7ae
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 MINPASSWDLENGTH 5
56 #define BUFSIZE 512
58 static int findpty(char **slave)
60 int master;
61 #ifndef HAVE_GRANTPT
62 static fstring line;
63 void *dirp;
64 char *dpname;
65 #endif /* !HAVE_GRANTPT */
67 #if defined(HAVE_GRANTPT)
68 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 1) {
69 grantpt(master);
70 unlockpt(master);
71 *slave = ptsname(master);
72 if (*slave == NULL) {
73 DEBUG(0,("findpty: Unable to create master/slave pty pair.\n"));
74 /* Stop fd leak on error. */
75 close(master);
76 return -1;
77 } else {
78 DEBUG(10, ("findpty: Allocated slave pty %s\n", *slave));
79 return (master);
82 #else /* HAVE_GRANTPT */
83 fstrcpy( line, "/dev/ptyXX" );
85 dirp = OpenDir(NULL, "/dev", False);
86 if (!dirp)
87 return(-1);
88 while ((dpname = ReadDirName(dirp)) != NULL) {
89 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
90 DEBUG(3,("pty: try to open %s, line was %s\n", dpname, line ) );
91 line[8] = dpname[3];
92 line[9] = dpname[4];
93 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
94 DEBUG(3,("pty: opened %s\n", line ) );
95 line[5] = 't';
96 *slave = line;
97 CloseDir(dirp);
98 return (master);
102 CloseDir(dirp);
103 #endif /* HAVE_GRANTPT */
104 return (-1);
107 static int dochild(int master,char *slavedev, char *name, char *passwordprogram, BOOL as_root)
109 int slave;
110 struct termios stermios;
111 struct passwd *pass = Get_Pwnam(name,True);
112 int gid;
113 int uid;
115 if (pass == NULL) {
116 DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n",
117 name));
118 return False;
121 gid = pass->pw_gid;
122 uid = pass->pw_uid;
123 #ifdef HAVE_SETRESUID
124 setresuid(0,0,0);
125 #else
126 setuid(0);
127 #endif
129 /* Start new session - gets rid of controlling terminal. */
130 if (setsid() < 0) {
131 DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
132 return(False);
135 /* Open slave pty and acquire as new controlling terminal. */
136 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0) {
137 DEBUG(3,("More weirdness, could not open %s\n",
138 slavedev));
139 return(False);
141 #ifdef I_PUSH
142 ioctl(slave, I_PUSH, "ptem");
143 ioctl(slave, I_PUSH, "ldterm");
144 #elif defined(TIOCSCTTY)
145 if (ioctl(slave,TIOCSCTTY,0) <0) {
146 DEBUG(3,("Error in ioctl call for slave pty\n"));
147 /* return(False); */
149 #endif
151 /* Close master. */
152 close(master);
154 /* Make slave stdin/out/err of child. */
156 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
157 DEBUG(3,("Could not re-direct stdin\n"));
158 return(False);
160 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
161 DEBUG(3,("Could not re-direct stdout\n"));
162 return(False);
164 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
165 DEBUG(3,("Could not re-direct stderr\n"));
166 return(False);
168 if (slave > 2) close(slave);
170 /* Set proper terminal attributes - no echo, canonical input processing,
171 no map NL to CR/NL on output. */
173 if (tcgetattr(0, &stermios) < 0) {
174 DEBUG(3,("could not read default terminal attributes on pty\n"));
175 return(False);
177 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
178 stermios.c_lflag |= ICANON;
179 stermios.c_oflag &= ~(ONLCR);
180 if (tcsetattr(0, TCSANOW, &stermios) < 0) {
181 DEBUG(3,("could not set attributes of pty\n"));
182 return(False);
185 /* make us completely into the right uid */
186 if (!as_root) {
187 #ifdef HAVE_SETRESUID
188 setresgid(0,0,0);
189 setresuid(0,0,0);
190 setresgid(gid,gid,gid);
191 setresuid(uid,uid,uid);
192 #else
193 setuid(0);
194 seteuid(0);
195 setgid(gid);
196 setegid(gid);
197 setuid(uid);
198 seteuid(uid);
199 #endif
202 DEBUG(10, ("Invoking '%s' as password change program.\n", passwordprogram));
204 /* execl() password-change application */
205 if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
206 DEBUG(3,("Bad status returned from %s\n",passwordprogram));
207 return(False);
209 return(True);
212 static int expect(int master,char *expected,char *buf)
214 int n, m;
216 n = 0;
217 buf[0] = 0;
218 while (1) {
219 if (n >= BUFSIZE-1) {
220 return False;
223 /* allow 4 seconds for some output to appear */
224 m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000);
225 if (m < 0)
226 return False;
228 n += m;
229 buf[n] = 0;
232 pstring s1,s2;
233 pstrcpy(s1,buf);
234 pstrcpy(s2,expected);
235 if (do_match(s1, s2, False))
236 return(True);
241 static void pwd_sub(char *buf)
243 string_sub(buf,"\\n","\n");
244 string_sub(buf,"\\r","\r");
245 string_sub(buf,"\\s"," ");
246 string_sub(buf,"\\t","\t");
249 static void writestring(int fd,char *s)
251 int l;
253 l = strlen (s);
254 write (fd, s, l);
258 static int talktochild(int master, char *chatsequence)
260 char buf[BUFSIZE];
261 int count=0;
262 char *ptr=chatsequence;
263 fstring chatbuf;
265 *buf = 0;
266 sleep(1);
268 while (next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) {
269 BOOL ok=True;
270 count++;
271 pwd_sub(chatbuf);
272 if (!strequal(chatbuf,"."))
273 ok = expect(master,chatbuf,buf);
275 if (lp_passwd_chat_debug())
276 DEBUG(100,("talktochild: chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
278 if (!ok) {
279 DEBUG(3,("response %d incorrect\n",count));
280 return(False);
283 if (!next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) break;
284 pwd_sub(chatbuf);
285 if (!strequal(chatbuf,"."))
286 writestring(master,chatbuf);
288 if (lp_passwd_chat_debug())
289 DEBUG(100,("talktochild: sendbuf=[%s]\n",chatbuf));
292 if (count<1) return(False);
294 return (True);
298 static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root)
300 char *slavedev;
301 int master;
302 pid_t pid, wpid;
303 int wstat;
304 BOOL chstat = False;
306 /* allocate a pseudo-terminal device */
307 if ((master = findpty (&slavedev)) < 0) {
308 DEBUG(3,("Cannot Allocate pty for password change: %s\n",name));
309 return(False);
312 if ((pid = fork()) < 0) {
313 DEBUG(3,("Cannot fork() child for password change: %s\n",name));
314 close(master);
315 return(False);
318 /* we now have a pty */
319 if (pid > 0){ /* This is the parent process */
320 if ((chstat = talktochild(master, chatsequence)) == False) {
321 DEBUG(3,("Child failed to change password: %s\n",name));
322 kill(pid, SIGKILL); /* be sure to end this process */
325 if ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
326 DEBUG(3,("The process is no longer waiting!\n\n"));
327 close(master);
328 return(False);
331 close(master);
333 if (pid != wpid) {
334 DEBUG(3,("We were waiting for the wrong process ID\n"));
335 return(False);
337 if (WIFEXITED(wstat) == 0) {
338 DEBUG(3,("The process exited while we were waiting\n"));
339 return(False);
341 if (WEXITSTATUS(wstat) != 0) {
342 DEBUG(3,("The status of the process exiting was %d\n", wstat));
343 return(False);
346 } else {
347 /* CHILD */
350 * Lose any oplock capabilities.
352 set_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
353 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
355 /* make sure it doesn't freeze */
356 alarm(20);
358 if (as_root)
359 become_root(False);
360 DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid()));
361 chstat = dochild(master, slavedev, name, passwordprogram, as_root);
363 if (as_root)
364 unbecome_root(False);
367 if (chstat)
368 DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
369 return (chstat);
373 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
375 pstring passwordprogram;
376 pstring chatsequence;
377 int i;
378 int len;
380 strlower(name);
381 DEBUG(3,("Password change for user: %s\n",name));
383 #if DEBUG_PASSWORD
384 DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass));
385 #endif
387 /* Take the passed information and test it for minimum criteria */
388 /* Minimum password length */
389 if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */
391 DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
392 return (False); /* inform the user */
395 /* Password is same as old password */
396 if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
398 DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
399 return (False); /* inform the user */
402 pstrcpy(passwordprogram,lp_passwd_program());
403 pstrcpy(chatsequence,lp_passwd_chat());
405 if (!*chatsequence) {
406 DEBUG(2,("Null chat sequence - no password changing\n"));
407 return(False);
410 if (!*passwordprogram) {
411 DEBUG(2,("Null password program - no password changing\n"));
412 return(False);
416 * Check the old and new passwords don't contain any control
417 * characters.
420 len = strlen(oldpass);
421 for(i = 0; i < len; i++) {
422 if (iscntrl((int)oldpass[i])) {
423 DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n"));
424 return False;
428 len = strlen(newpass);
429 for(i = 0; i < len; i++) {
430 if (iscntrl((int)newpass[i])) {
431 DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n"));
432 return False;
436 string_sub(passwordprogram,"%u",name);
437 all_string_sub(passwordprogram,"%o",oldpass);
438 all_string_sub(passwordprogram,"%n",newpass);
440 string_sub(chatsequence,"%u",name);
441 all_string_sub(chatsequence,"%o",oldpass);
442 all_string_sub(chatsequence,"%n",newpass);
443 return(chat_with_program(passwordprogram,name,chatsequence, as_root));
446 #else /* ALLOW_CHANGE_PASSWORD */
447 BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root)
449 DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
450 return(False);
452 #endif /* ALLOW_CHANGE_PASSWORD */
454 /***********************************************************
455 Code to check the lanman hashed password.
456 ************************************************************/
458 BOOL check_lanman_password(char *user, uchar *pass1,
459 uchar *pass2, struct smb_passwd **psmbpw)
461 static uchar null_pw[16];
462 uchar unenc_new_pw[16];
463 uchar unenc_old_pw[16];
464 struct smb_passwd *smbpw;
466 *psmbpw = NULL;
468 become_root(0);
469 smbpw = getsmbpwnam(user);
470 unbecome_root(0);
472 if (smbpw == NULL)
474 DEBUG(0,("check_lanman_password: getsmbpwnam returned NULL\n"));
475 return False;
478 if (smbpw->acct_ctrl & ACB_DISABLED)
480 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
481 return False;
484 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
486 uchar no_pw[14];
487 memset(no_pw, '\0', 14);
488 E_P16(no_pw, null_pw);
489 smbpw->smb_passwd = null_pw;
490 } else if (smbpw->smb_passwd == NULL) {
491 DEBUG(0,("check_lanman_password: no lanman password !\n"));
492 return False;
495 /* Get the new lanman hash. */
496 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
498 /* Use this to get the old lanman hash. */
499 D_P16(unenc_new_pw, pass1, unenc_old_pw);
501 /* Check that the two old passwords match. */
502 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
504 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
505 return False;
508 *psmbpw = smbpw;
509 return True;
512 /***********************************************************
513 Code to change the lanman hashed password.
514 It nulls out the NT hashed password as it will
515 no longer be valid.
516 ************************************************************/
518 BOOL change_lanman_password(struct smb_passwd *smbpw, uchar *pass1, uchar *pass2)
520 static uchar null_pw[16];
521 uchar unenc_new_pw[16];
522 BOOL ret;
524 if (smbpw == NULL)
526 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
527 return False;
530 if (smbpw->acct_ctrl & ACB_DISABLED)
532 DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->smb_name));
533 return False;
536 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
538 uchar no_pw[14];
539 memset(no_pw, '\0', 14);
540 E_P16(no_pw, null_pw);
541 smbpw->smb_passwd = null_pw;
542 } else if (smbpw->smb_passwd == NULL) {
543 DEBUG(0,("change_lanman_password: no lanman password !\n"));
544 return False;
547 /* Get the new lanman hash. */
548 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
550 smbpw->smb_passwd = unenc_new_pw;
551 smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */
553 /* Now write it into the file. */
554 become_root(0);
555 ret = mod_smbpwd_entry(smbpw,False);
556 unbecome_root(0);
558 return ret;
561 /***********************************************************
562 Code to check and change the OEM hashed password.
563 ************************************************************/
564 BOOL pass_oem_change(char *user,
565 uchar *lmdata, uchar *lmhash,
566 uchar *ntdata, uchar *nthash)
568 fstring new_passwd;
569 struct smb_passwd *sampw;
570 BOOL ret = check_oem_password( user, lmdata, lmhash, ntdata, nthash,
571 &sampw,
572 new_passwd, sizeof(new_passwd));
575 * At this point we have the new case-sensitive plaintext
576 * password in the fstring new_passwd. If we wanted to synchronise
577 * with UNIX passwords we would call a UNIX password changing
578 * function here. However it would have to be done as root
579 * as the plaintext of the old users password is not
580 * available. JRA.
583 if ( ret && lp_unix_password_sync())
585 ret = chgpasswd(user,"", new_passwd, True);
588 if (ret)
590 ret = change_oem_password( sampw, new_passwd, False );
593 memset(new_passwd, 0, sizeof(new_passwd));
595 return ret;
598 /***********************************************************
599 Code to check the OEM hashed password.
601 this function ignores the 516 byte nt OEM hashed password
602 but does use the lm OEM password to check the nt hashed-hash.
604 ************************************************************/
605 BOOL check_oem_password(char *user,
606 uchar *lmdata, uchar *lmhash,
607 uchar *ntdata, uchar *nthash,
608 struct smb_passwd **psmbpw, char *new_passwd,
609 int new_passwd_size)
611 static uchar null_pw[16];
612 static uchar null_ntpw[16];
613 struct smb_passwd *smbpw = NULL;
614 int new_pw_len;
615 uchar new_ntp16[16];
616 uchar unenc_old_ntpw[16];
617 uchar new_p16[16];
618 uchar unenc_old_pw[16];
619 char no_pw[2];
621 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
623 become_root(False);
624 *psmbpw = smbpw = getsmbpwnam(user);
625 unbecome_root(False);
627 if (smbpw == NULL)
629 DEBUG(0,("check_oem_password: getsmbpwnam returned NULL\n"));
630 return False;
633 if (smbpw->acct_ctrl & ACB_DISABLED)
635 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
636 return False;
639 /* construct a null password (in case one is needed */
640 no_pw[0] = 0;
641 no_pw[1] = 0;
642 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
644 /* check for null passwords */
645 if (smbpw->smb_passwd == NULL)
647 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
649 smbpw->smb_passwd = null_pw;
651 else
653 DEBUG(0,("check_oem_password: no lanman password !\n"));
654 return False;
658 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
660 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
662 smbpw->smb_nt_passwd = null_pw;
664 else
666 DEBUG(0,("check_oem_password: no ntlm password !\n"));
667 return False;
672 * Call the hash function to get the new password.
674 SamOEMhash( (uchar *)lmdata, (uchar *)smbpw->smb_passwd, True);
677 * The length of the new password is in the last 4 bytes of
678 * the data buffer.
681 new_pw_len = IVAL(lmdata, 512);
682 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1)
684 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
685 return False;
688 if (nt_pass_set)
691 * nt passwords are in unicode
693 int uni_pw_len = new_pw_len;
694 char *pw;
695 new_pw_len /= 2;
696 pw = unistrn2((uint16*)(&lmdata[512-uni_pw_len]), new_pw_len);
697 memcpy(new_passwd, pw, new_pw_len+1);
699 else
701 memcpy(new_passwd, &lmdata[512-new_pw_len], new_pw_len);
702 new_passwd[new_pw_len] = '\0';
706 * To ensure we got the correct new password, hash it and
707 * use it as a key to test the passed old password.
710 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
712 if (!nt_pass_set)
715 * Now use new_p16 as the key to see if the old
716 * password matches.
718 D_P16(new_p16 , lmhash, unenc_old_pw);
720 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
722 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
723 return False;
726 #ifdef DEBUG_PASSWORD
727 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
728 #endif
729 return True;
733 * Now use new_p16 as the key to see if the old
734 * password matches.
736 D_P16(new_ntp16, lmhash, unenc_old_pw);
737 D_P16(new_ntp16, nthash, unenc_old_ntpw);
739 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
741 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
742 return False;
745 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
747 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
748 return False;
750 #ifdef DEBUG_PASSWORD
751 DEBUG(100,("check_oem_password: password %s ok\n", new_passwd));
752 #endif
753 return True;
756 /***********************************************************
757 Code to change the oem password. Changes both the lanman
758 and NT hashes.
759 override = False, normal
760 override = True, override XXXXXXXXXX'd password
761 ************************************************************/
763 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd, BOOL override)
765 int ret;
766 uchar new_nt_p16[16];
767 uchar new_p16[16];
769 nt_lm_owf_gen(new_passwd, new_nt_p16, new_p16);
771 smbpw->smb_passwd = new_p16;
772 smbpw->smb_nt_passwd = new_nt_p16;
774 /* Now write it into the file. */
775 become_root(0);
776 ret = mod_smbpwd_entry(smbpw,override);
777 unbecome_root(0);
779 memset(new_passwd, '\0', strlen(new_passwd));
781 return ret;