static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root) { char *slavedev; int master; pid_t pid, wpid; int wstat; BOOL chstat = False; /* allocate a pseudo-terminal device */ if ((master = findpty (&slavedev)) < 0) { DEBUG(3,("Cannot Allocate pty for password change: %s\n",name)); return(False); } /* * We need to temporarily stop CatchChild from eating * SIGCLD signals as it also eats the exit status code. JRA. */ CatchChildLeaveStatus(); #ifdef __uClinux__ /* Hmmm, need to check this one further... */ DEBUG(0,("%s(%d): vfork()ing\n",__FILE__,__LINE__)); if ((pid = vfork()) < 0) { #else if ((pid = fork()) < 0) { #endif DEBUG(3,("Cannot fork() child for password change: %s\n",name)); close(master); CatchChild(); return(False); } /* we now have a pty */ if (pid > 0){ /* This is the parent process */ if ((chstat = talktochild(master, chatsequence)) == False) { DEBUG(3,("Child failed to change password: %s\n",name)); kill(pid, SIGKILL); /* be sure to end this process */ } while((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { if(errno == EINTR) { errno = 0; continue; } break; } if (wpid < 0) { DEBUG(3,("The process is no longer waiting!\n\n")); close(master); CatchChild(); return(False); } /* * Go back to ignoring children. */ CatchChild(); close(master); if (pid != wpid) { DEBUG(3,("We were waiting for the wrong process ID\n")); return(False); } if (WIFEXITED(wstat) == 0) { DEBUG(3,("The process exited while we were waiting\n")); return(False); } if (WEXITSTATUS(wstat) != 0) { DEBUG(3,("The status of the process exiting was %d\n", wstat)); return(False); } } else { /* CHILD */ /* * Lose any oplock capabilities. */ set_process_capability(KERNEL_OPLOCK_CAPABILITY, False); set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY, False); /* make sure it doesn't freeze */ alarm(20); if (as_root) become_root(False); DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid())); chstat = dochild(master, slavedev, name, passwordprogram, as_root); /* * The child should never return from dochild() .... */ DEBUG(0,("chat_with_program: Error: dochild() returned %d\n", chstat )); exit(1); } if (chstat) DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name)); return (chstat); } BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root) { pstring passwordprogram; pstring chatsequence; size_t i; size_t len; strlower(name); DEBUG(3,("Password change for user: %s\n",name)); #if DEBUG_PASSWORD DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); #endif /* Take the passed information and test it for minimum criteria */ /* Minimum password length */ if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */ { DEBUG(0,("Password Change: user %s, New password is shorter than minimum password length = %d\n", name, lp_min_passwd_length())); return (False); /* inform the user */ } /* Password is same as old password */ if (strcmp(oldpass,newpass) == 0) /* don't allow same password */ { DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */ return (False); /* inform the user */ } pstrcpy(passwordprogram,lp_passwd_program()); pstrcpy(chatsequence,lp_passwd_chat()); if (!*chatsequence) { DEBUG(2,("Null chat sequence - no password changing\n")); return(False); } if (!*passwordprogram) { DEBUG(2,("Null password program - no password changing\n")); return(False); } /* * Check the old and new passwords don't contain any control * characters. */ len = strlen(oldpass); for(i = 0; i < len; i++) { if (iscntrl((int)oldpass[i])) { DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n")); return False; } } len = strlen(newpass); for(i = 0; i < len; i++) { if (iscntrl((int)newpass[i])) { DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n")); return False; } } pstring_sub(passwordprogram,"%u",name); /* note that we do NOT substitute the %o and %n in the password program as this would open up a security hole where the user could use a new password containing shell escape characters */ pstring_sub(chatsequence,"%u",name); all_string_sub(chatsequence,"%o",oldpass,sizeof(pstring)); all_string_sub(chatsequence,"%n",newpass,sizeof(pstring)); return(chat_with_program(passwordprogram,name,chatsequence, as_root)); }
static bool chat_with_program(char *passwordprogram, const struct passwd *pass, char *chatsequence, bool as_root) { char *slavedev = NULL; int master; pid_t pid, wpid; int wstat; bool chstat = False; void (*saved_handler)(int); if (pass == NULL) { DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n")); return False; } /* allocate a pseudo-terminal device */ if ((master = findpty(&slavedev)) < 0) { DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name)); return (False); } /* * We need to temporarily stop CatchChild from eating * SIGCLD signals as it also eats the exit status code. JRA. */ saved_handler = CatchChildLeaveStatus(); if ((pid = fork()) < 0) { DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name)); SAFE_FREE(slavedev); close(master); (void)CatchSignal(SIGCLD, saved_handler); return (False); } /* we now have a pty */ if (pid > 0) { /* This is the parent process */ /* Don't need this anymore in parent. */ SAFE_FREE(slavedev); if ((chstat = talktochild(master, chatsequence)) == False) { DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name)); kill(pid, SIGKILL); /* be sure to end this process */ } while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { if (errno == EINTR) { errno = 0; continue; } break; } if (wpid < 0) { DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n")); close(master); (void)CatchSignal(SIGCLD, saved_handler); return (False); } /* * Go back to ignoring children. */ (void)CatchSignal(SIGCLD, saved_handler); close(master); if (pid != wpid) { DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n")); return (False); } if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) { DEBUG(3, ("chat_with_program: The process exited with status %d \ while we were waiting\n", WEXITSTATUS(wstat))); return (False); }
static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root) { char *slavedev; int master; pid_t pid, wpid; int wstat; BOOL chstat = False; /* allocate a pseudo-terminal device */ if ((master = findpty (&slavedev)) < 0) { DEBUG(3,("Cannot Allocate pty for password change: %s\n",name)); return(False); } /* * We need to temporarily stop CatchChild from eating * SIGCLD signals as it also eats the exit status code. JRA. */ CatchChildLeaveStatus(); if ((pid = fork()) < 0) { DEBUG(3,("Cannot fork() child for password change: %s\n",name)); close(master); CatchChild(); return(False); } /* we now have a pty */ if (pid > 0){ /* This is the parent process */ if ((chstat = talktochild(master, chatsequence)) == False) { DEBUG(3,("Child failed to change password: %s\n",name)); kill(pid, SIGKILL); /* be sure to end this process */ } while((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { if(errno == EINTR) { errno = 0; continue; } break; } if (wpid < 0) { DEBUG(3,("The process is no longer waiting!\n\n")); close(master); CatchChild(); return(False); } /* * Go back to ignoring children. */ CatchChild(); close(master); if (pid != wpid) { DEBUG(3,("We were waiting for the wrong process ID\n")); return(False); } if (WIFEXITED(wstat) == 0) { DEBUG(3,("The process exited while we were waiting\n")); return(False); } if (WEXITSTATUS(wstat) != 0) { DEBUG(3,("The status of the process exiting was %d\n", wstat)); return(False); } } else { /* CHILD */ /* * Lose any oplock capabilities. */ set_process_capability(KERNEL_OPLOCK_CAPABILITY, False); set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY, False); /* make sure it doesn't freeze */ alarm(20); if (as_root) become_root(False); DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid())); chstat = dochild(master, slavedev, name, passwordprogram, as_root); /* * The child should never return from dochild() .... */ DEBUG(0,("chat_with_program: Error: dochild() returned %d\n", chstat )); exit(1); } if (chstat) DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name)); return (chstat); }
int main(int argc, char **argv) { FILE *fp; struct timeval tv; fd_set fds; char buf[1024]; char realcons[1024]; char *p; char *logfile; char *pidfile; int rotate; int dontfork; int ptm, pts; int realfd; int n, m, i; int todo; fp = NULL; logfile = LOGFILE; pidfile = NULL; rotate = 0; dontfork = 0; while ((i = getopt(argc, argv, "cdsl:p:rv")) != EOF) switch(i) { case 'l': logfile = optarg; break; case 'r': rotate = 1; break; case 'v': printf("%s\n", Version); exit(0); break; case 'p': pidfile = optarg; break; case 'c': createlogfile = 1; break; case 'd': dontfork = 1; break; case 's': syncalot = 1; break; default: usage(); break; } if (optind < argc) usage(); signal(SIGTERM, handler); signal(SIGQUIT, handler); signal(SIGINT, handler); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGTSTP, SIG_IGN); /* * Open console device directly. */ if (consolename(realcons, sizeof(realcons)) < 0) return 1; if (strcmp(realcons, "/dev/tty0") == 0) strcpy(realcons, "/dev/tty1"); if (strcmp(realcons, "/dev/vc/0") == 0) strcpy(realcons, "/dev/vc/1"); if ((realfd = open_nb(realcons)) < 0) { fprintf(stderr, "bootlogd: %s: %s\n", buf, strerror(errno)); return 1; } /* * Grab a pty, and redirect console messages to it. */ ptm = -1; pts = -1; buf[0] = 0; if (findpty(&ptm, &pts, buf) < 0) { fprintf(stderr, "bootlogd: cannot allocate pseudo tty: %s\n", strerror(errno)); return 1; } (void)ioctl(0, TIOCCONS, NULL); #if 1 /* Work around bug in 2.1/2.2 kernels. Fixed in 2.2.13 and 2.3.18 */ if ((n = open("/dev/tty0", O_RDWR)) >= 0) { (void)ioctl(n, TIOCCONS, NULL); close(n); } #endif if (ioctl(pts, TIOCCONS, NULL) < 0) { fprintf(stderr, "bootlogd: ioctl(%s, TIOCCONS): %s\n", buf, strerror(errno)); return 1; } /* * Fork and write pidfile if needed. */ if (!dontfork) { pid_t child_pid = fork(); switch (child_pid) { case -1: /* I am parent and the attempt to create a child failed */ fprintf(stderr, "bootlogd: fork failed: %s\n", strerror(errno)); exit(1); break; case 0: /* I am the child */ break; default: /* I am parent and got child's pid */ exit(0); break; } setsid(); } if (pidfile) { unlink(pidfile); if ((fp = fopen(pidfile, "w")) != NULL) { fprintf(fp, "%d\n", (int)getpid()); fclose(fp); } fp = NULL; } /* * Read the console messages from the pty, and write * to the real console and the logfile. */ while (!got_signal) { /* * We timeout after 5 seconds if we still need to * open the logfile. There might be buffered messages * we want to write. */ tv.tv_sec = 0; tv.tv_usec = 500000; FD_ZERO(&fds); FD_SET(ptm, &fds); if (select(ptm + 1, &fds, NULL, NULL, &tv) == 1) { /* * See how much space there is left, read. */ if ((n = read(ptm, inptr, endptr - inptr)) >= 0) { /* * Write data (in chunks if needed) * to the real output device. */ m = n; p = inptr; while (m > 0) { i = write(realfd, p, m); if (i >= 0) { m -= i; p += i; continue; } /* * Handle EIO (somebody hung * up our filedescriptor) */ realfd = write_err(pts, realfd, realcons, errno); if (realfd >= 0) continue; got_signal = 1; /* Not really */ break; } /* * Increment buffer position. Handle * wraps, and also drag output pointer * along if we cross it. */ inptr += n; if (inptr - n < outptr && inptr > outptr) outptr = inptr; if (inptr >= endptr) inptr = ringbuf; if (outptr >= endptr) outptr = ringbuf; } } /* * Perhaps we need to open the logfile. */ if (fp == NULL && access(logfile, F_OK) == 0) { if (rotate) { snprintf(buf, sizeof(buf), "%s~", logfile); rename(logfile, buf); } fp = fopen(logfile, "a"); } if (fp == NULL && createlogfile) fp = fopen(logfile, "a"); if (inptr >= outptr) todo = inptr - outptr; else todo = endptr - outptr; if (fp && todo) writelog(fp, (unsigned char *)outptr, todo); } if (fp) { if (!didnl) fputc('\n', fp); fclose(fp); } close(pts); close(ptm); close(realfd); return 0; }