static int opentty(const char *ttyn, int flags) { int i, j = 0; int failopenlogged = 0; while (j < 10 && (i = open(ttyn, flags)) == -1) { if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) { syslog(LOG_ERR, "open %s: %m", ttyn); failopenlogged = 1; } j++; sleep(60); } if (i == -1) { syslog(LOG_ERR, "open %s: %m", ttyn); return 0; } else { if (login_tty(i) < 0) { if (daemon(0,0) < 0) { syslog(LOG_ERR,"daemon: %m"); close(i); return 0; } if (login_tty(i) < 0) { syslog(LOG_ERR, "login_tty %s: %m", ttyn); close(i); return 0; } } return 1; } }
static int opentty(const char *tty, int flags) { int i; int failopenlogged = 0; while ((i = open(tty, flags)) == -1) { if (!failopenlogged) { syslog(LOG_ERR, "open %s: %m", tty); failopenlogged = 1; } sleep(60); } if (login_tty(i) < 0) { if (daemon(0,0) < 0) { syslog(LOG_ERR,"daemon: %m"); close(i); return 0; } if (login_tty(i) < 0) { syslog(LOG_ERR, "login_tty %s: %m", tty); close(i); return 0; } } return 1; }
int forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp) { int master, slave, pid; if (openpty(&master, &slave, name, termp, winp) == -1) return (-1); switch (pid = fork()) { case -1: return (-1); case 0: /* * child */ (void) close(master); login_tty(slave); return (0); } /* * parent */ *amaster = master; (void) close(slave); return (pid); }
/* * Start a session and allocate a controlling terminal. * Only called by children of init after forking. */ static void open_console(void) { int fd; /* * Try to open /dev/console. Open the device with O_NONBLOCK to * prevent potential blocking on a carrier. */ revoke(_PATH_CONSOLE); if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) { (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); if (login_tty(fd) == 0) return; close(fd); } /* No luck. Log output to file if possible. */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { stall("cannot open null device."); _exit(1); } if (fd != STDIN_FILENO) { dup2(fd, STDIN_FILENO); close(fd); } fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd == -1) dup2(STDIN_FILENO, STDOUT_FILENO); else if (fd != STDOUT_FILENO) { dup2(fd, STDOUT_FILENO); close(fd); } dup2(STDOUT_FILENO, STDERR_FILENO); }
int forkpty(int* master, char* name, const termios* t, const winsize* ws) { int slave; if (openpty(master, &slave, name, t, ws) == -1) { return -1; } pid_t pid = fork(); if (pid == -1) { close(*master); close(slave); return -1; } if (pid == 0) { // Child. close(*master); if (login_tty(slave) == -1) { _exit(1); } return 0; } // Parent. close(slave); return pid; }
int start_session(int vty, int argc, char **argv) { int fd; char *t; close(0); close(1); close(2); revoke(ttys[vty].tty); fd=open(ttys[vty].tty,O_RDWR); dup2(fd,0); dup2(fd,1); dup2(fd,2); if(fd>2) close(fd); login_tty(fd); setpgid(0,getpid()); putenv("TERM=xterm"); putenv("HOME=/"); putenv("PATH=/stand:/bin:/usr/bin:/sbin:."); signal(SIGHUP,SIG_DFL); signal(SIGINT,SIG_DFL); signal(SIGQUIT,SIG_DFL); signal(SIGTERM,SIG_DFL); chdir("/"); t=(char *)(rindex(ttys[vty].tty,'/')+1); printf("\n\n\nStarting session on %s.\n",t); ttys[vty].func(argc,argv); _exit(0); }
long long channel_forkpty(int fd[3], int master, int slave) { long long pid; if (!ttyname(slave)) return -1; fd[0] = fd[1] = master; fd[2] = -1; pid = fork(); switch (pid) { case -1: close(slave); close(master); return -1; case 0: close(master); #ifdef HASLOGINTTY if (!ttyname(slave)) global_die(111); if (login_tty(slave) == -1) global_die(111); #else if (_login_tty(slave) == -1) global_die(111); #endif return 0; default: coe_enable(master); blocking_disable(master); close(slave); return pid; } }
int forkpty (int *amaster, char *name, const struct termios *termp, const struct winsize *winp) { int master, slave, pid; if (openpty (&master, &slave, name, termp, winp) == -1) return -1; switch (pid = fork ()) { case -1: close (master); close (slave); return -1; case 0: /* Child. */ close (master); if (login_tty (slave)) _exit (1); return 0; default: /* Parent. */ *amaster = master; close (slave); return pid; } }
static int _gftp_ptys_open (int fdm, int fds, char *pts_name) { if (login_tty (fds) < 0) return (GFTP_EFATAL); return (fds); }
LinuxProcess::LinuxProcess(Screen *scr, Font *f, const char *cmdline) : Process(scr, f, cmdline) { fd_master=posix_openpt(O_RDWR|O_NOCTTY); ppid=getpid(); // Process ID of parent cpid=-1; // Get child PID later s = scr; assert( fd_master >= 0 ); // Did master terminal open? assert( unlockpt(fd_master) >= 0 ); // Unlock PTY assert( grantpt(fd_master) >= 0 ); // Grant PTY assert( (pts=ptsname(fd_master)) != NULL ); // Get slave name // Create a child process to use the slave pty assert( (cpid=fork()) >= 0); // set size... struct winsize size; int ret; memset(&size, 0, sizeof(size)); size.ws_row = scr->sy; size.ws_col = scr->sx; ret = ioctl(fd_master, TIOCSWINSZ, &size); if (ret != 0) { logfile && fprintf(logfile, "failed to set window size\n"); } if(cpid == 0) { int fd_slave=-1; // Slave PTY assert( close(fd_master) >= 0 ); // Ditch master PTY fd_slave=open(pts,O_RDWR); assert( fd_slave >= 0); // This will dup fd over stdin,out,err then close fd // This function needs compilation with -lutil assert( login_tty(fd_slave) >= 0 ); // we are simulating the 'linux' terminal setenv("TERM", "linux", 1); // todo: should also disable UTF8 int s = system(cmdline); exit(WEXITSTATUS(s)); } isActive = true; resetConsole(); }
int set_pty(char *pty_name) { int fd; if ((fd = open(pty_name, O_RDWR, 0)) == -1 || login_tty(fd) == -1) return (0); return (set_noecho(STDIN_FILENO)); }
static void dodock() { char *argv[]={"termim-next", NULL}; (void)close(tube[0]); (void)close(master2); login_tty(slave2); execvp(argv[0], argv); warn("%s", "ime"); fail(); }
int forkpty(int *pm, char *name, const struct termios *tio, const struct winsize *ws) { int m, s, ec=0, p[2], cs; pid_t pid=-1; sigset_t set, oldset; if (openpty(&m, &s, name, tio, ws) < 0) return -1; sigfillset(&set); pthread_sigmask(SIG_BLOCK, &set, &oldset); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); if (pipe2(p, O_CLOEXEC)) { close(s); goto out; } pid = fork(); if (!pid) { close(m); close(p[0]); if (login_tty(s)) { write(p[1], &errno, sizeof errno); _exit(127); } close(p[1]); pthread_setcancelstate(cs, 0); pthread_sigmask(SIG_SETMASK, &oldset, 0); return 0; } close(s); close(p[1]); if (read(p[0], &ec, sizeof ec) > 0) { int status; waitpid(pid, &status, 0); pid = -1; errno = ec; } close(p[0]); out: if (pid > 0) *pm = m; else close(m); pthread_setcancelstate(cs, 0); pthread_sigmask(SIG_SETMASK, &oldset, 0); return pid; }
static void script_start_shell(ckl_script_t *s, int mpty, int spty) { int rv = 0; close(mpty); rv = login_tty(spty); if (rv) { perror("login_tty(slave) failed:"); exit(EXIT_FAILURE); } execl(s->shell, s->shell, "-i", NULL); perror("execl of shell failed!"); exit(EXIT_FAILURE); }
/* * Start a session and allocate a controlling terminal. * Only called by children of init after forking. */ static void setctty(const char *name) { int fd; revoke(name); if ((fd = open(name, O_RDWR)) == -1) { stall("can't open %s: %m", name); _exit(1); } if (login_tty(fd) == -1) { stall("can't get %s for controlling terminal: %m", name); _exit(1); } }
static int setctty(const char *name) { int fd; revoke(name); if ((fd = open(name, O_RDWR)) == -1) { exit(1); } if (login_tty(fd) == -1) { exit(1); } return fd; }
int main () { int master; int slave; /* Open a pseudo-terminal, as a master-slave pair. */ { int res = openpty (&master, &slave, NULL, NULL, NULL); if (res != 0) { fprintf (stderr, "openpty returned %d\n", res); return 1; } } /* Create a new session and make it the controlling tty of this session. */ { int res = login_tty (slave); if (res < 0) { fprintf (stderr, "login_tty failed\n"); return 1; } } /* From here on, we cannot use stderr for error messages any more. If a test fails, just abort. */ /* Check that fd = 0, 1, 2 are now open to the controlling terminal for the current process and that it is a session of its own. */ { int fd; for (fd = 0; fd < 3; fd++) if (!(tcgetpgrp (fd) == getpid ())) abort (); for (fd = 0; fd < 3; fd++) { int sid = tcgetsid (fd); if (!(sid == -1 ? errno == ENOSYS : sid == getpid ())) abort (); } } return 0; }
pid_t forkpty(int *amaster, char *name, const struct termios *termp, const struct winsize *winp) { int child; int ret = openpty(amaster, &child, name, termp, winp); if(ret) return -1; pid_t rv = fork(); if(rv) return rv; login_tty(child); // In child dup2(child, 0); dup2(child, 1); dup2(child, 2); close(child); return 0; }
static void doshell(char **av) { const char *shell; shell = getenv("SHELL"); if (shell == NULL) shell = _PATH_BSHELL; (void)close(master); login_tty(slave); if (av[0]) { execvp(av[0], av); warn("%s", av[0]); } else { execl(shell, shell, "-i", (char *)NULL); warn("%s", shell); } fail(); }
pid_t forkpty(int *masterp, char *name, struct termios * termp, struct winsize * winp) { int master, slave; char ptname[PATH_MAX]; pid_t pid; master = open_master(ptname, sizeof (ptname)); if (master < 0) { return -1; } slave = open_slave(ptname); if (slave < 0) { close(master); return -1; } if (name) strcpy(name, ptname); if (termp) tcsetattr(slave, TCSAFLUSH, termp); if (winp) ioctl(slave, TIOCSWINSZ, winp); pid = fork(); if (pid < 0) { close(slave); close(master); return -1; } else if (pid == 0) { /* child/slave */ close(master); login_tty(slave); return 0; } /* parent/master */ *masterp = master; close(slave); return pid; }
static int exec_pty(const char *mode, const char *command, struct channel_data_struct *cdata) { switch(cdata->pid = fork()) { case -1: close(cdata->pty_master); close(cdata->pty_slave); fprintf(stderr, "Failed to fork\n"); return SSH_ERROR; case 0: close(cdata->pty_master); if (login_tty(cdata->pty_slave) != 0) { exit(1); } execl("/bin/sh", "sh", mode, command, NULL); exit(0); default: close(cdata->pty_slave); /* pty fd is bi-directional */ cdata->child_stdout = cdata->child_stdin = cdata->pty_master; } return SSH_OK; }
int main() { int fdm, fds; int pid, i; fd_set set; char buf[64]; if (openpty(&fdm, &fds, NULL, NULL, NULL)) exit(1); if ((pid = fork()) < 0) exit(2); if (!pid) { /*child*/ close(fdm); login_tty(fds); execl("/bin/sh", "sh", NULL); exit(3); } /*father: copy stdin/stdout to/from master */ close(fds); system("stty raw -echo"); FD_ZERO(&set); while (waitpid(pid, &i, WNOHANG)!=pid) { FD_SET(0,&set); FD_SET(fdm,&set); select(fdm+1, &set, NULL, NULL, NULL); if (FD_ISSET(0, &set)) { i = read(0, buf, 64); if (i>0) write(fdm, buf, i); } if (FD_ISSET(fdm, &set)) { i = read(fdm, buf, 64); if (i>0) write(1, buf, i); } } system("stty sane"); exit(0); }
/* * launch a program inside a subwindow, and report its return status when done */ static int launch_subwin(WINDOW **actionwin, char **args, struct winsize *win, int flags, const char *scmd, const char **errstr) { int n, i; int selectfailed; int status, master, slave; fd_set active_fd_set, read_fd_set; pid_t child, pid; char ibuf[MAXBUF]; char pktdata; char *cp, *ncp; struct termios rtt, tt; struct timeval tmo; static int do_tioccons = 2; (void)tcgetattr(STDIN_FILENO, &tt); if (openpty(&master, &slave, NULL, &tt, win) == -1) { *errstr = "openpty() failed"; return -1; } rtt = tt; /* ignore tty signals until we're done with subprocess setup */ ttysig_ignore = 1; ioctl(master, TIOCPKT, &ttysig_ignore); /* Try to get console output into our pipe */ if (do_tioccons) { if (ioctl(slave, TIOCCONS, &do_tioccons) == 0 && do_tioccons == 2) { /* test our output - we don't want it grabbed */ write(1, " \b", 2); ioctl(master, FIONREAD, &do_tioccons); if (do_tioccons != 0) { do_tioccons = 0; ioctl(slave, TIOCCONS, &do_tioccons); } else do_tioccons = 1; } } if (logfp) fflush(logfp); if (script) fflush(script); child = fork(); switch (child) { case -1: ttysig_ignore = 0; refresh(); *errstr = "fork() failed"; return -1; case 0: /* child */ (void)close(STDIN_FILENO); /* silently stop curses */ (void)close(STDOUT_FILENO); (void)open("/dev/null", O_RDWR, 0); dup2(STDIN_FILENO, STDOUT_FILENO); endwin(); (void)close(master); rtt = tt; rtt.c_lflag |= (ICANON|ECHO); (void)tcsetattr(slave, TCSANOW, &rtt); login_tty(slave); if (logfp) { fprintf(logfp, "executing: %s\n", scmd); fclose(logfp); logfp = NULL; } if (script) { fprintf(script, "%s\n", scmd); fclose(script); script = NULL; } if (strcmp(args[0], "cd") == 0 && strcmp(args[2], "&&") == 0) { target_chdir_or_die(args[1]); args += 3; } if (flags & RUN_XFER_DIR) target_chdir_or_die(xfer_dir); /* * If target_prefix == "", the chroot will fail, but * that's ok, since we don't need it then. */ if (flags & RUN_CHROOT && *target_prefix() && chroot(target_prefix()) != 0) warn("chroot(%s) for %s", target_prefix(), *args); else { execvp(*args, args); warn("execvp %s", *args); } _exit(EXIT_FAILURE); // break; /* end of child */ default: /* * parent: we've set up the subprocess. * forward tty signals to its process group. */ ttysig_forward = child; ttysig_ignore = 0; break; } /* * Now loop transferring program output to screen, and keyboard * input to the program. */ FD_ZERO(&active_fd_set); FD_SET(master, &active_fd_set); FD_SET(STDIN_FILENO, &active_fd_set); for (selectfailed = 0;;) { if (selectfailed) { const char mmsg[] = "select(2) failed but no child died?"; if (logfp) (void)fprintf(logfp, mmsg); errx(1, mmsg); } read_fd_set = active_fd_set; tmo.tv_sec = flags & RUN_SILENT ? 20 : 2; tmo.tv_usec = 0; i = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &tmo); if (i == 0 && *actionwin == NULL && (flags & RUN_SILENT) == 0) *actionwin = show_cmd(scmd, win); if (i < 0) { if (errno != EINTR) { warn("select"); if (logfp) (void)fprintf(logfp, "select failure: %s\n", strerror(errno)); selectfailed = 1; } } else for (i = 0; i < FD_SETSIZE; ++i) { if (!FD_ISSET(i, &read_fd_set)) continue; n = read(i, ibuf, sizeof ibuf - 1); if (n <= 0) { if (n < 0) warn("read"); continue; } ibuf[n] = 0; cp = ibuf; if (i == STDIN_FILENO) { (void)write(master, ibuf, (size_t)n); if (!(rtt.c_lflag & ECHO)) continue; } else { pktdata = ibuf[0]; if (pktdata != 0) { if (pktdata & TIOCPKT_IOCTL) memcpy(&rtt, ibuf, sizeof(rtt)); continue; } cp += 1; } if (*cp == 0 || flags & RUN_SILENT) continue; if (logfp) { fprintf(logfp, "%s", cp); fflush(logfp); } if (*actionwin == NULL) *actionwin = show_cmd(scmd, win); /* posix curses is braindead wrt \r\n so... */ for (ncp = cp; (ncp = strstr(ncp, "\r\n")); ncp += 2) { ncp[0] = '\n'; ncp[1] = '\r'; } waddstr(*actionwin, cp); wrefresh(*actionwin); } pid = wait4(child, &status, WNOHANG, 0); if (pid == child && (WIFEXITED(status) || WIFSIGNALED(status))) break; } close(master); close(slave); if (logfp) fflush(logfp); /* from here on out, we take tty signals ourselves */ ttysig_forward = 0; reset_prog_mode(); if (WIFEXITED(status)) { *errstr = msg_string(MSG_Command_failed); return WEXITSTATUS(status); } if (WIFSIGNALED(status)) { *errstr = msg_string(MSG_Command_ended_on_signal); return WTERMSIG(status); } return 0; }
static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) { libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty); STATE_AO_GC(bl->ao); int rc, r; char *const env[] = { "TERM", "vt100", NULL }; if (bl->openpty.rc) { rc = bl->openpty.rc; goto out; } /* * We need to present the bootloader's tty as a pty slave that xenconsole * can access. Since the bootloader itself needs a pty slave, * we end up with a connection like this: * * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader * * where we copy characters between the two master fds, as well as * listening on the bootloader's fifo for the results. */ char *dom_console_xs_path; char dom_console_slave_tty_path[PATH_MAX]; rc = setup_xenconsoled_pty(egc, bl, &dom_console_slave_tty_path[0], sizeof(dom_console_slave_tty_path)); if (rc) goto out; char *dompath = libxl__xs_get_dompath(gc, bl->domid); if (!dompath) { rc = ERROR_FAIL; goto out; } dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath); rc = libxl__xs_write(gc, XBT_NULL, dom_console_xs_path, "%s", dom_console_slave_tty_path); if (rc) { LOGE(ERROR,"xs write console path %s := %s failed", dom_console_xs_path, dom_console_slave_tty_path); rc = ERROR_FAIL; goto out; } bl->deathcheck.what = "stopping bootloader"; bl->deathcheck.domid = bl->domid; bl->deathcheck.callback = bootloader_domaindeath; rc = libxl__domaindeathcheck_start(gc, &bl->deathcheck); if (rc) goto out; if (bl->console_available) bl->console_available(egc, bl); int bootloader_master = libxl__carefd_fd(bl->ptys[0].master); int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master); libxl_fd_set_nonblock(CTX, bootloader_master, 1); libxl_fd_set_nonblock(CTX, xenconsole_master, 1); bl->keystrokes.writefd = bl->display.readfd = bootloader_master; bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty"; bl->keystrokes.readfd = bl->display.writefd = xenconsole_master; bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty"; bl->keystrokes.ao = ao; bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT; bl->keystrokes.copywhat = GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid); bl->keystrokes.callback = bootloader_keystrokes_copyfail; bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail; /* pollhup gets called with errnoval==-1 which is not otherwise * possible since errnos are nonnegative, so it's unambiguous */ rc = libxl__datacopier_start(&bl->keystrokes); if (rc) goto out; bl->display.ao = ao; bl->display.maxsz = BOOTLOADER_BUF_IN; bl->display.copywhat = GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid); bl->display.callback = bootloader_display_copyfail; bl->display.callback_pollhup = bootloader_display_copyfail; rc = libxl__datacopier_start(&bl->display); if (rc) goto out; LOG(DEBUG, "executing bootloader: %s", bl->args[0]); for (const char **blarg = bl->args; *blarg; blarg++) LOG(DEBUG, " bootloader arg: %s", *blarg); struct termios termattr; pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); if (pid == -1) { rc = ERROR_FAIL; goto out; } if (!pid) { /* child */ r = login_tty(libxl__carefd_fd(bl->ptys[0].slave)); if (r) { LOGE(ERROR, "login_tty failed"); exit(-1); } libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env); exit(-1); } /* parent */ /* * On Solaris, the master pty side does not have terminal semantics, * so don't try to set any attributes, as it will fail. */ #if !defined(__sun__) tcgetattr(bootloader_master, &termattr); cfmakeraw(&termattr); tcsetattr(bootloader_master, TCSANOW, &termattr); #endif return; out: bootloader_callback(egc, bl, rc); }
int main(int argc, char **argv) { int fdm, fds, pid, exitval = 0; struct minipc_ch *ch; struct pty_counts counters = {0,}; /* First, open the pty */ if (openpty(&fdm, &fds, NULL, NULL, NULL) < 0) { fprintf(stderr, "%s: openpty(): %s\n", argv[0], strerror(errno)); exit(1); } /* Run a shell and let it go by itself before we open the rpc */ fprintf(stderr, "%s: Running a sub-shell in a new pty\n", argv[0]); if ((pid = fork()) < 0) { fprintf(stderr, "%s: fork(): %s\n", argv[0], strerror(errno)); exit(1); } if (!pid) { /* Child: become a shell and disappear... */ close(fdm); login_tty(fds); execl("/bin/sh", "sh", NULL); fprintf(stderr, "%s: exec(/bin/sh): %s\n", argv[0], strerror(errno)); exit(1); } /* Open the RPC server channel */ ch = minipc_server_create(PTY_RPC_NAME, 0); if (!ch) { fprintf(stderr, "%s: rpc_open(): %s\n", argv[0], strerror(errno)); exit(1); } /* Log file for diagnostics */ { char name[] = "/tmp/pty-server.XXXXXX"; int logfd; FILE *logf; logfd = mkstemp(name); if (logfd >= 0) { logf = fdopen(logfd, "w"); if (logf) minipc_set_logfile(ch, logf); } } /* Register your functions: all our RPC is split to another source */ if (pty_export_functions(ch, fdm, &counters)) { fprintf(stderr, "%s: exporting RPC functions: %s\n", argv[0], strerror(errno)); exit(1); } /* * Now, we must mirror stdin/stdout to the pty master, with RPC too. * The first step is horribly changing the termios of our tty */ close(fds); system("stty raw -echo"); while (waitpid(pid, NULL, WNOHANG) != pid) { fd_set set; int nfd, i; char buf[256]; /* ask the RPC engine its current fdset and augment it */ minipc_server_get_fdset(ch, &set); FD_SET(STDIN_FILENO, &set); FD_SET(fdm, &set); /* wait for any of the FD to be active */ nfd = select(64 /* Hmmm... */, &set, NULL, NULL, NULL); if (nfd < 0 && errno == EINTR) continue; if (nfd < 0) { fprintf(stderr, "%s: select(): %s\n", argv[0], strerror(errno)); exitval = 1; break; } /* Handle fdm and fds by just mirroring stuff and counting */ if (FD_ISSET(STDIN_FILENO, &set)) { i = read(0, buf, sizeof(buf)); if (i > 0) { write(fdm, buf, i); counters.in += i; } nfd--; } if (FD_ISSET(fdm, &set)) { i = read(fdm, buf, sizeof(buf)); if (i > 0) { write(1, buf, i); counters.out += i; } nfd--; } /* If there are no more active fd, loop over */ if (!nfd) continue; /* * If we are there, there has been an RPC call. * We tell the library to use a 0 timeout, since we know * for sure that at least one of its descriptors is pending. */ minipc_server_action(ch, 0); } /* The child shell exited, reset the tty and exit. Let RPC die out */ system("stty sane"); exit(exitval); }
void exec_shell(struct conf *cfp, int fd, int parentfd) { int master, slave; pid_t pid; char line[MAXPATHLEN]; char *tty; #ifdef HAVE_UTMP_H struct utmp ut; #elif HAVE_UTMPX_H struct utmpx ut; struct timeval tv; #endif #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H) memset(&ut, 0, sizeof(ut)); #endif if (pipe(pipechld) < 0) { syslog(LOG_ERR, "exec_shell() pipe %m"); _exit(1); } (void)non_blocking(pipechld[0]); (void)non_blocking(pipechld[1]); if (openpty(&master, &slave, line, NULL, NULL) == -1) { syslog(LOG_ERR, "openpty %m"); _exit(1) ; } /* * pts/x compatible */ if ((tty = strstr(line, "/dev/"))) tty += 5; else tty = line; #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H) if (cfp->utmp) { if (cfp->utname) { #ifdef HAVE_UTMP_H (void)strncpy(ut.ut_name, cfp->utname, sizeof(ut.ut_name)-1); ut.ut_name[sizeof(ut.ut_name)-1] = '\0'; #elif HAVE_UTMPX_H (void)strncpy(ut.ut_user, cfp->utname, sizeof(ut.ut_user)-1); ut.ut_user[sizeof(ut.ut_user)-1] = '\0'; #endif } else { struct passwd *pw; pw = get_pwentry(cfp->havesetuser ? cfp->setuser : \ 0); #ifdef HAVE_UTMP_H (void)strncpy(ut.ut_name, pw->pw_name, sizeof(ut.ut_name)-1); ut.ut_name[sizeof(ut.ut_name)-1] = '\0'; #elif HAVE_UTMPX_H (void)strncpy(ut.ut_user, pw->pw_name, sizeof(ut.ut_user)-1); ut.ut_user[sizeof(ut.ut_user)-1] = '\0'; #endif } (void)strncpy(ut.ut_line, tty, sizeof(ut.ut_line)-1); ut.ut_line[sizeof(ut.ut_line)-1] = '\0'; if (cfp->uthost) { (void)strncpy(ut.ut_host, cfp->uthost, sizeof(ut.ut_host)-1); ut.ut_host[sizeof(ut.ut_host)-1] = '\0'; } #ifdef HAVE_UTMP_H (void)time(&ut.ut_time); #elif HAVE_UTMPX_H (void)gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec; (void)strncpy(ut.ut_id, ut.ut_line, sizeof(ut.ut_id)-1); ut.ut_line[sizeof(ut.ut_line)-1] = '\0'; ut.ut_pid = getpid(); ut.ut_type = USER_PROCESS; #endif } #endif /* * overwriting signal disposition */ (void)signal(SIGCHLD, sig_chld); switch (pid = fork()) { case -1: syslog(LOG_ERR, "forkpty: %m"); _exit(1); case 0: (void)close(parentfd); (void)close(pipechld[0]); (void)close(pipechld[1]); (void)close(master); (void)close(fd); (void)login_tty(slave); #ifdef HAVE_UTMP_H login(&ut); #elif HAVE_UTMPX_H setutxent(); (void)pututxline(&ut); #endif set_privileges(cfp); /* * SUIP PROGRAM HERE */ #ifdef __NetBSD__ (void)execl(_PATH_BSHELL,"sh", "-c",cfp->suipfile,(char *)NULL); #else (void)execl(_PATH_BSHELL, "sh", "-p", "-c", cfp->suipfile, (char *)NULL); #endif _exit(127); default: { int ctrls; int exit_status = 0; (void)close(slave); /* * trying to open a control channel * control_create() returns the number of bytes which were * written * select_fd() returns -1 if errors exist you can check errno */ if (control_create(&ctrls, fd) == 1) { if (select_fd(cfp, fd, master, ctrls, parentfd) < 0) exit_status = 1; } else { syslog(LOG_ERR, "can't open ctrl chan"); exit_status = 1; } #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H) if (cfp->utmp) { #ifdef HAVE_UTMP_H if (!logout(tty)) { syslog(LOG_ERR, "unable to logout on %s", tty); exit_status = 1; } else logwtmp(tty, "", ""); #elif HAVE_UTMPX_H ut.ut_type = DEAD_PROCESS; (void)gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec; (void)memset(&ut.ut_user, 0, sizeof(ut.ut_user)); setutxent(); if (pututxline(&ut) == NULL) { syslog(LOG_ERR, "unable to logout on %s (utmpx)", tty); exit_status = 1; } endutxent(); #endif } #endif cleanup(line); _exit(exit_status); }} /* * never reached */ _exit(1); }
static void console_login(char *conf, char *msg) { int i, cfirst = TRUE; char *nl = "\r\n"; struct termios ttybuf; char *p, **cargv; int fd; syslog(LOG_DEBUG, "Performing console login: %s", msg); sigemptyset(&sig_zero); if (login_running != NONEXISTENT && login_running != STARTUP) kill(loginpid, SIGKILL); if (console_running != NONEXISTENT) { if (cfirst) kill(consolepid, SIGHUP); else kill(consolepid, SIGKILL); cfirst = FALSE; } if (x_running != NONEXISTENT) kill(xpid, SIGTERM); x_stop_wait(); unlink(dmpidf); p = getconf(conf, "ttylogin"); if (p == NULL) { fprintf(stderr, "dm: Can't find login command line\n"); exit(1); } cargv = parseargs(p, NULL, NULL, NULL); setpgid(0, 0); /* We have to reset the tty pgrp */ if (tcsetpgrp(0, getpgrp()) == -1) syslog(LOG_ERR, "tcsetpgrp failed in console login (%s)", strerror(errno)); tcflush(0, TCIOFLUSH); (void) tcgetattr(0, &ttybuf); ttybuf.c_lflag |= (ICANON | ISIG | ECHO); (void) tcsetattr(0, TCSADRAIN, &ttybuf); (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL); max_fd = sysconf(_SC_OPEN_MAX); for (i = 3; i < max_fd; i++) close(i); if (msg) fprintf(stderr, "%s", msg); else fprintf(stderr, "%s", nl); fd = open("/dev/console", O_RDWR); if (fd >= 0) login_tty(fd); execv(p, cargv); fprintf(stderr, "dm: Unable to start console login: %s\n", strerror(errno)); _exit(1); }
int main(int argc, char **argv) { char *consoletty, *p; char **dmargv, **xargv, **consoleargv = NULL, **loginargv; char xpidf[256], line[16], buf[256]; fd_set readfds; int pgrp, file, tries, count, redir = TRUE; char dpyacl[40]; Display *dpy; XHostAddress *hosts, localhost; int nhosts, dpynum = 0; struct stat hostsinfo; Bool state; time_t now, last_console_failure = 0; struct sigaction sigact; sigset_t mask; #if defined(SRIOCSREDIR) || defined(TIOCCONS) int on; #endif int fd; int conspipe[2]; XIOErrorHandler xioerror_handler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; (void) sigemptyset(&sig_zero); /* Create a localhost entity for access control purposes. */ localhost.family = FamilyLocalHost; localhost.length = 0; localhost.address = ""; /* * Note about setting environment variables in dm: * * All environment variables passed to dm and set in dm are * subsequently passed to any children of dm. This is usually * true of processes that exec in children, so that's not a * big surprise. * * However, xlogin is one of the children dm forks, and it goes * to lengths to ensure that the environments of users logging in * are ISOLATED from xlogin's own environment. Therefore, do not * expect that setting an environment variable here will reach the * user unless you have gone to lengths to make sure that xlogin * passes it on. Put another way, if you set a new environment * variable here, consider whether or not it should be seen by the * user. If it should, go modify verify.c as well. Consider also * whether the variable should be seen _only_ by the user. If so, * make the change only in xlogin, and not here. * * As an added complication, xlogin _does_ pass environment variables * on to the pre-login options. Therefore, if you set an environment * variable that should _not_ be seen, you must filter it in xlogin.c. * * Confused? Too bad. I'm in a nasty, if verbose, mood this year. * * General summary: * * If you add an environment variable here there are three likely * possibilities: * * 1. It's for the user only, not needed by any of dm's children. * --> Don't set it here. Set it in verify.c for users and in * --> xlogin.c for the pre-login options, if appropriate. * * 2. It's for dm and its children only, and _should not_ be seen * by the user or pre-login options. * --> You must filter the option from the pre-login options * --> in xlogin.c. No changes to verify.c are required. * * 3. It's for dm and the user and the pre-login options. * --> You must pass the option explicitly to the user in * --> verify.c. No changes to xlogin.c are required. * * --- cfields */ #ifdef notdef putenv("LD_LIBRARY_PATH=/usr/openwin/lib"); putenv("OPENWINHOME=/usr/openwin"); #endif if (argc < 2) { fprintf(stderr, "dm: first argument must be configuration file\n"); sleep(60); exit(1); } conf = argv[1]; if (argc != 4 && (argc != 5 || strcmp(argv[3], "-noconsole"))) { fprintf(stderr, "usage: %s configfile logintty [-noconsole] consoletty\n", argv[0]); console_login(conf, NULL); } if (argc == 5) redir = FALSE; /* parse argument lists */ /* ignore argv[2] */ consoletty = argv[argc - 1]; #ifdef SOLARIS /* On Solaris, use the console tty name for the utmp line field, * as the Solaris finger requires an actual device name there. * Elsewhere, we will use the display name (see below). */ utmp_line = consoletty; #endif openlog("dm", 0, LOG_USER); /* We use options from the config file rather than taking * them from the command line because the current command * line form is gross (why???), and I don't see a good way * to extend it without making things grosser or breaking * backwards compatibility. So, we take a line from the * config file and use real parsing. */ p = getconf(conf, "dm"); if (p != NULL) { dmargv = parseargs(p, NULL, NULL, NULL); while (*dmargv) { if (!strcmp(*dmargv, "-display")) { dmargv++; if (*dmargv) { dpynum = atoi(*(dmargv) + 1); dmargv++; } } else dmargv++; } } p = getconf(conf, "X"); if (p == NULL) console_login(conf, "\ndm: Can't find X command line\n"); xargv = parseargs(p, NULL, NULL, NULL); p = getconf(conf, "console"); if (p == NULL) console_login(conf, "\ndm: Can't find console command line\n"); /* We will pass the read side of the pipe created below to console * on descriptor 3. */ consoleargv = parseargs(p, "-inputfd", "3", NULL); /* Signal Setup */ sigact.sa_handler = SIG_IGN; sigaction(SIGTSTP, &sigact, NULL); sigaction(SIGTTIN, &sigact, NULL); sigaction(SIGTTOU, &sigact, NULL); /* so that X pipe errors don't nuke us */ sigaction(SIGPIPE, &sigact, NULL); sigact.sa_handler = shutdown; sigaction(SIGFPE, &sigact, NULL); sigact.sa_handler = die; sigaction(SIGHUP, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigact.sa_handler = child; sigaction(SIGCHLD, &sigact, NULL); sigact.sa_handler = catchalarm; sigaction(SIGALRM, &sigact, NULL); strcpy(line, "/dev/"); strcat(line, consoletty); fd = open(line, O_RDWR); if (fd == -1) { syslog(LOG_ERR, "Cannot open %s: %m", line); /* This probably won't work, but it seems to be the appropriate punt location. */ console_login(conf, "Cannot open tty.\n"); } if (login_tty(fd) == -1) syslog(LOG_ERR, "Cannot set the console as a login terminal (%s)", strerror(errno)); else { /* Set the console characteristics so we don't lose later */ setpgid(0, pgrp = getpid()); /* Reset the tty pgrp */ if (tcsetpgrp(0, pgrp) == -1) syslog(LOG_ERR, "tcsetpgrp failed for console terminal (%s)", strerror(errno)); } /* save our pid file */ writepid(dmpidf, getpid()); /* Fire up X */ xpid = 0; for (tries = 0; tries < 3; tries++) { syslog(LOG_DEBUG, "Starting X, try #%d", tries + 1); x_running = STARTUP; sigact.sa_handler = xready; sigaction(SIGUSR1, &sigact, NULL); switch (fork_and_store(&xpid)) { case 0: if (fcntl(2, F_SETFD, 1) == -1) close(2); (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0); /* ignoring SIGUSR1 will cause the server to send us a SIGUSR1 * when it is ready to accept connections */ sigact.sa_handler = SIG_IGN; sigaction(SIGUSR1, &sigact, NULL); p = *xargv; *xargv = "X"; execv(p, xargv); fprintf(stderr, "dm: X server failed exec: %s\n", strerror(errno)); _exit(1); case -1: fprintf(stderr, "dm: Unable to fork to start X server: %s\n", strerror(errno)); break; default: sprintf(xpidf, xpids, dpynum); writepid(xpidf, xpid); if (x_running == STARTUP) { alarm(X_START_WAIT); alarm_running = RUNNING; sigsuspend(&sig_zero); } if (x_running != RUNNING) { syslog(LOG_DEBUG, "X failed to start; alarm_running=%d", alarm_running); if (alarm_running == NONEXISTENT) fprintf(stderr, "dm: Unable to start X\n"); else fprintf(stderr, "dm: X failed to become ready\n"); /* If X wouldn't run, it could be that an existing X * process hasn't shut down. Wait X_STOP_WAIT seconds * for that to happen. */ x_stop_wait(); } sigact.sa_handler = SIG_IGN; sigaction(SIGUSR1, &sigact, NULL); } if (x_running == RUNNING) break; } alarm(0); if (x_running != RUNNING) { syslog(LOG_DEBUG, "Giving up on starting X."); console_login(conf, "\nUnable to start X, doing console login " "instead.\n"); } /* Tighten up security a little bit. Remove all hosts from X's * access control list, assuming /etc/X0.hosts does not exist or * has zero length. If it does exist with nonzero length, this * behavior is not wanted. The desired effect of removing all hosts * is that only connections from the Unix domain socket will be * allowed. * More secure code using Xau also exists, but there wasn't * time to completely flesh it out and resolve a couple of * issues. This code is probably good enough, but we'll see. * Maybe next time. * This code has the added benefit of leaving an X display * connection open, owned by dm. This provides a less-hacky * solution to the config_console problem, where if config_console * is the first program run on user login, it causes the only * X app running at the time, console, to exit, thus resetting * the X server. Thus this code also allows the removal of the * hack in xlogin that attempts to solve the same problem, but * fails on the RS/6000 for reasons unexplored. * P.S. Don't run this code under Solaris 2.2- (2.3 is safe). * Removing all hosts from the acl on that server results in * no connections, not even from the Unix domain socket, being * allowed. --- cfields */ sprintf(dpyacl, xhosts, dpynum); sprintf(dpyname, ":%d", dpynum); #ifndef SOLARIS /* Use the display name for the utmp line field, except on Solaris. */ utmp_line = dpyname; #endif /* Put in our own error handler, open the display, then reset the handler. */ xioerror_handler = XSetIOErrorHandler(handle_xioerror); dpy = XOpenDisplay(dpyname); XSetIOErrorHandler(xioerror_handler); if (dpy != NULL && (stat(dpyacl, &hostsinfo) || hostsinfo.st_size == 0)) { hosts = XListHosts(dpy, &nhosts, &state); if (hosts != NULL) { XRemoveHosts(dpy, hosts, nhosts); XFree(hosts); } XAddHost(dpy, &localhost); XFlush(dpy); } /* else if (dpy == NULL) * Could've sworn the X server was running now. * Follow the original code path. No need introducing new bugs * to this hairy code, just preserve the old behavior as though * this code had never been added. */ /* set up the console pty */ if (openpty(&console_master_fd, &console_slave_fd, NULL, NULL, NULL) == -1) console_login(conf, "Cannot allocate pseudo-terminal\n"); if (redir) { /* Redirect /dev/console output to the pty slave. */ #ifdef SRIOCSREDIR on = open("/dev/console", O_RDONLY); if (on >= 0) { ioctl(on, SRIOCSREDIR, console_slave_fd); close(on); } #else #ifdef TIOCCONS on = 1; ioctl(console_slave_fd, TIOCCONS, &on); #endif #endif } /* Set up the console pipe. */ if (pipe(conspipe) == -1) console_login(conf, "Cannot create pipe for console\n"); /* start up console */ start_console(console_master_fd, conspipe[0], consoleargv); /* Set up to invoke xlogin. */ p = getconf(conf, "login"); if (p == NULL) console_login(conf, "\ndm: Can't find login command line\n"); loginargv = parseargs(p, "-line", utmp_line, NULL); /* Fire up the X login */ for (tries = 0; tries < 3; tries++) { syslog(LOG_DEBUG, "Starting xlogin, try #%d", tries + 1); login_running = STARTUP; sigact.sa_handler = loginready; sigaction(SIGUSR1, &sigact, NULL); switch (fork_and_store(&loginpid)) { case 0: max_fd = sysconf(_SC_OPEN_MAX); for (file = 3; file < max_fd; file++) { if (file != conspipe[1]) close(file); } setsid(); file = open("/dev/null", O_RDONLY); if (file >= 0) { dup2(file, 0); if (file != 0) close(file); } file = conspipe[1]; if (file == -1) file = open("/dev/null", O_WRONLY); if (file >= 0) { if (file != 1) dup2(file, 1); if (file != 2) dup2(file, 2); if (file != 1 && file != 2) close(file); } (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0); /* ignoring SIGUSR1 will cause xlogin to send us a SIGUSR1 * when it is ready */ sigact.sa_handler = SIG_IGN; sigaction(SIGUSR1, &sigact, NULL); /* dm ignores sigpipe; because of this, all of the children (ie, */ /* the entire session) inherit this unless we fix it now */ sigact.sa_handler = SIG_DFL; sigaction(SIGPIPE, &sigact, NULL); execv(loginargv[0], loginargv); fprintf(stderr, "dm: X login failed exec: %s\n", strerror(errno)); _exit(1); case -1: fprintf(stderr, "dm: Unable to fork to start X login: %s\n", strerror(errno)); break; default: alarm(LOGIN_START_WAIT); alarm_running = RUNNING; while (login_running == STARTUP && alarm_running == RUNNING) sigsuspend(&sig_zero); if (login_running != RUNNING) { syslog(LOG_DEBUG, "xlogin failed to start; alarm_running=%d", alarm_running); kill(loginpid, SIGKILL); if (alarm_running != NONEXISTENT) fprintf(stderr, "dm: Unable to start Xlogin\n"); else fprintf(stderr, "dm: Xlogin failed to become ready\n"); } } if (login_running == RUNNING) break; } sigact.sa_handler = SIG_IGN; sigaction(SIGUSR1, &sigact, NULL); alarm(0); if (login_running != RUNNING) { syslog(LOG_DEBUG, "Giving up on starting xlogin."); console_login(conf, "\nUnable to start xlogin, doing console login " "instead.\n"); } /* main loop. Wait for SIGCHLD, waking up every minute anyway. */ (void) sigemptyset(&sig_cur); (void) sigaddset(&sig_cur, SIGCHLD); (void) sigprocmask(SIG_BLOCK, &sig_cur, NULL); while (1) { /* Wait for something to hapen */ if (console_failed) { /* if no console is running, we must copy bits from the console * (master side of pty) to the real console to appear as black * bar messages. */ FD_ZERO(&readfds); FD_SET(console_master_fd, &readfds); (void) sigprocmask(SIG_SETMASK, &sig_zero, &mask); count = select(console_master_fd + 1, &readfds, NULL, NULL, NULL); (void) sigprocmask(SIG_BLOCK, &mask, NULL); if (count > 0 && FD_ISSET(console_master_fd, &readfds)) { file = read(console_master_fd, buf, sizeof(buf)); if (file != -1) write(1, buf, file); } } else { alarm(60); sigsuspend(&sig_zero); } if (login_running == STARTUP) { (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL); console_login(conf, "\nConsole login requested.\n"); } if (console_running == FAILED) { console_running = NONEXISTENT; time(&now); if (now - last_console_failure <= 3) { /* Give up on console. Set the console characteristics so * we don't lose later. */ syslog(LOG_ERR, "Giving up on the console"); setpgid(0, pgrp = getpid()); /* Reset the tty pgrp */ tcsetpgrp(0, pgrp); console_failed = TRUE; } else last_console_failure = now; } if (console_running == NONEXISTENT && !console_failed) start_console(console_master_fd, conspipe[0], consoleargv); if (login_running == NONEXISTENT || x_running == NONEXISTENT) { syslog(LOG_DEBUG, "login_running=%d, x_running=%d, quitting", login_running, x_running); (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL); cleanup(utmp_line); _exit(0); } } }
int main(int argc, char **argv) { struct sockaddr_storage peer_sa; struct sockaddr *peer = (struct sockaddr *)&peer_sa; int peerlen = sizeof(peer_sa); char user[1024]; char luser[1024]; char term[1024]; int port; struct passwd *pw; int err; char opt; char host[NI_MAXHOST]; char buf[4096]; int len; struct pollfd pfd[3]; struct winsize winsize; uint16_t winbuf[4]; int i; int master, slave; char *tty; pam_handle_t *handle; struct pam_conv conv = {conv_h, NULL}; const void *item; char *pamuser; int pid; argv0 = argv[0]; /* Process options */ while((opt = getopt(argc, argv, "+")) != -1) { switch(opt) { default: syslog(LOG_ERR, "Unknown option!"); usage(); return 1; } } if(optind != argc) { syslog(LOG_ERR, "Too many arguments!"); usage(); return 1; } /* Check source of connection */ if(getpeername(0, peer, &peerlen)) { syslog(LOG_ERR, "Can't get address of peer: %m"); return 1; } /* Unmap V4MAPPED addresses */ if(peer->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)peer)->sin6_addr)) { ((struct sockaddr_in *)peer)->sin_addr.s_addr = ((struct sockaddr_in6 *)peer)->sin6_addr.s6_addr32[3]; peer->sa_family = AF_INET; } /* Lookup hostname */ if((err = getnameinfo(peer, peerlen, host, sizeof(host), NULL, 0, 0))) { syslog(LOG_ERR, "Error resolving address: %s", gai_strerror(err)); return 1; } /* Check if connection comes from a privileged port */ switch(peer->sa_family) { case AF_INET: port = ntohs(((struct sockaddr_in *)peer)->sin_port); break; case AF_INET6: port = ntohs(((struct sockaddr_in6 *)peer)->sin6_port); break; default: port = -1; break; } if(port != -1 && (port < 512 || port >= 1024)) { syslog(LOG_ERR, "Connection from %s on illegal port %d.", host, port); return 1; } /* Wait for NULL byte */ if(read(0, buf, 1) != 1 || *buf) { syslog(LOG_ERR, "Didn't receive NULL byte from %s: %m\n", host); return 1; } /* Read usernames and terminal info */ if(readtonull(0, user, sizeof(user)) <= 0 || readtonull(0, luser, sizeof(luser)) <= 0) { syslog(LOG_ERR, "Error while receiving usernames from %s: %m", host); return 1; } if(readtonull(0, term, sizeof(term)) <= 0) { syslog(LOG_ERR, "Error while receiving terminal from %s: %m", host); return 1; } syslog(LOG_NOTICE, "Connection from %s@%s for %s", user, host, luser); /* We need to have a pty before we can use PAM */ if(openpty(&master, &slave, 0, 0, &winsize) != 0) { syslog(LOG_ERR, "Could not open pty: %m"); return 1; } tty = ttyname(slave); /* Start PAM */ if((err = pam_start("rlogin", luser, &conv, &handle)) != PAM_SUCCESS) { safewrite(1, "Authentication failure\n", 23); syslog(LOG_ERR, "PAM error: %s", pam_strerror(handle, err)); return 1; } pam_set_item(handle, PAM_USER, luser); pam_set_item(handle, PAM_RUSER, user); pam_set_item(handle, PAM_RHOST, host); pam_set_item(handle, PAM_TTY, tty); /* Write NULL byte to client so we can give a login prompt if necessary */ if(safewrite(1, "", 1) == -1) { syslog(LOG_ERR, "Unable to write NULL byte: %m"); return 1; } /* Try to authenticate */ err = pam_authenticate(handle, 0); /* PAM might ask for a new password */ if(err == PAM_NEW_AUTHTOK_REQD) { err = pam_chauthtok(handle, PAM_CHANGE_EXPIRED_AUTHTOK); if(err == PAM_SUCCESS) err = pam_authenticate(handle, 0); } if(err != PAM_SUCCESS) { safewrite(1, "Authentication failure\n", 23); syslog(LOG_ERR, "PAM error: %s", pam_strerror(handle, err)); return 1; } /* Check account */ err = pam_acct_mgmt(handle, 0); if(err != PAM_SUCCESS) { safewrite(1, "Authentication failure\n", 23); syslog(LOG_ERR, "PAM error: %s", pam_strerror(handle, err)); return 1; } /* PAM can map the user to a different user */ err = pam_get_item(handle, PAM_USER, &item); if(err != PAM_SUCCESS) { syslog(LOG_ERR, "PAM error: %s", pam_strerror(handle, err)); return 1; } pamuser = strdup((char *)item); if(!pamuser || !*pamuser) { syslog(LOG_ERR, "PAM didn't return a username?!"); return 1; } pw = getpwnam(pamuser); if (!pw) { syslog(LOG_ERR, "PAM_USER does not exist?!"); return 1; } if (setgid(pw->pw_gid)) { syslog(LOG_ERR, "setgid() failed: %m"); return 1; } if (initgroups(pamuser, pw->pw_gid)) { syslog(LOG_ERR, "initgroups() failed: %m"); return 1; } err = pam_setcred(handle, PAM_ESTABLISH_CRED); if(err != PAM_SUCCESS) { syslog(LOG_ERR, "PAM error: %s", pam_strerror(handle, err)); return 1; } /* Authentication succeeded */ pam_end(handle, PAM_SUCCESS); /* spawn login shell */ if((pid = fork()) < 0) { syslog(LOG_ERR, "fork() failed: %m"); return 1; } if(send(1, "\x80", 1, MSG_OOB) <= 0) { syslog(LOG_ERR, "Unable to write OOB \x80: %m"); return 1; } if(pid) { /* Parent process, still the rlogin server */ close(slave); /* Process input/output */ pfd[0].fd = 0; pfd[0].events = POLLIN | POLLERR | POLLHUP; pfd[1].fd = master; pfd[1].events = POLLIN | POLLERR | POLLHUP; for(;;) { errno = 0; if(poll(pfd, 2, -1) == -1) { if(errno == EINTR) continue; break; } if(pfd[0].revents) { len = read(0, buf, sizeof(buf)); if(len <= 0) break; /* Scan for control messages. Yes this is evil and should be done differently. */ for(i = 0; i < len - 11;) { if(buf[i++] == (char)0xFF) if(buf[i++] == (char)0xFF) if(buf[i++] == 's') if(buf[i++] == 's') { memcpy(winbuf, buf + i, 8); winsize.ws_row = ntohs(winbuf[0]); winsize.ws_col = ntohs(winbuf[1]); winsize.ws_xpixel = ntohs(winbuf[2]); winsize.ws_ypixel = ntohs(winbuf[3]); if(ioctl(master, TIOCSWINSZ, &winsize) == -1) break; memcpy(buf + i - 4, buf + i + 8, len - i - 8); i -= 4; len -= 12; } } if(safewrite(master, buf, len) == -1) break; pfd[0].revents = 0; } if(pfd[1].revents) { len = read(master, buf, sizeof(buf)); if(len <= 0) { errno = 0; break; } if(safewrite(1, buf, len) == -1) break; pfd[1].revents = 0; } } /* The end */ if(errno) { syslog(LOG_NOTICE, "Closing connection with %s@%s: %m", user, host); return 1; } else { syslog(LOG_NOTICE, "Closing connection with %s@%s", user, host); return 0; } close(master); } else { /* Child process, will become the shell */ char *speed; struct termios tios; char *envp[2]; /* Prepare tty for login */ close(master); if(login_tty(slave)) { syslog(LOG_ERR, "login_tty() failed: %m"); return 1; } /* Fix terminal type and speed */ tcgetattr(0, &tios); if((speed = strchr(term, '/'))) { *speed++ = '\0'; cfsetispeed(&tios, atoi(speed)); cfsetospeed(&tios, atoi(speed)); } tcsetattr(0, TCSADRAIN, &tios); /* Create environment */ asprintf(&envp[0], "TERM=%s", term); envp[1] = NULL; /* Spawn login process */ execle("/bin/login", "login", "-p", "-h", host, "-f", pamuser, NULL, envp); syslog(LOG_ERR, "Failed to spawn login process: %m"); return 1; } }
IProcess* UnixProcessImpl::Execute( wxEvtHandler* parent, const wxString& cmd, size_t flags, const wxString& workingDirectory, IProcessCallback* cb) { wxUnusedVar(flags); make_argv(cmd); if(argc == 0) { return NULL; } // fork the child process wxString curdir = wxGetCwd(); // Prentend that we are a terminal... int master, slave; openpty(&master, &slave, NULL, NULL, NULL); int rc = fork(); if(rc == 0) { login_tty(slave); close(master); // close the un-needed master end // at this point, slave is used as stdin/stdout/stderr // Child process if(workingDirectory.IsEmpty() == false) { wxSetWorkingDirectory(workingDirectory); } // execute the process execvp(argv[0], argv); // if we got here, we failed... exit(0); } else if(rc < 0) { // Error // restore the working directory wxSetWorkingDirectory(curdir); return NULL; } else { // Parent close(slave); freeargv(argv); argc = 0; // disable ECHO struct termios termio; tcgetattr(master, &termio); termio.c_lflag = ICANON; termio.c_oflag = ONOCR | ONLRET; tcsetattr(master, TCSANOW, &termio); // restore the working directory wxSetWorkingDirectory(curdir); UnixProcessImpl* proc = new UnixProcessImpl(parent); proc->m_callback = cb; proc->SetReadHandle(master); proc->SetWriteHandler(master); proc->SetPid(rc); proc->m_flags = flags; // Keep the creation flags if(!(proc->m_flags & IProcessCreateSync)) { proc->StartReaderThread(); } return proc; } }