#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "config.h"
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef PTYS_ARE_PTMX
# include <sys/resource.h> /* for struct rlimit */
# include <sys/stropts.h> /* for I_PUSH */
# define _NEW_TTY_CTRL /* to get proper defines in <termios.h> */
#endif
#define MAX_TIMEOUT 240
/* #define DEBUG */
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#ifndef IGNORE
#define IGNORE 1
#endif
#ifndef SAVE
#define SAVE 2
#endif
#ifndef RESTORE
#define RESTORE 3
#endif
#ifndef O_NDELAY
#define O_NDELAY O_NONBLOCK
#endif
extern char password[128];
extern char passphrase[128];
extern char * hom_str;
extern void print_dbg (const char *fmt, ...);
extern void get_pw();
extern void get_pp();
extern void privileges(int mode);
extern pid_t child_pid;
extern int opt_tarflag;
static char *ttydev = NULL; /* pty/tty name */
static struct stat ttyfd_stat; /* original status of the tty we will use */
static short changettyowner = 1;
/* ssh reads the password not from stdin, but from /dev/tty
* therefore, we put the subprocess on a virtual console (what a pain ...)
*/
/* the following two subroutines are from rxvt
*/
/*{{{ Acquire a pseudo-teletype from the system. */
/*
* On failure, returns -1.
* On success, returns the file descriptor.
*
* If successful, ttydev and ptydev point to the names of the
* master and slave parts
*/
/* INTPROTO */
static int
get_pty(void)
{
int fd;
char *ptydev;
#ifdef PTYS_ARE__GETPTY
if ((ptydev = ttydev = _getpty(&fd, O_RDWR | O_NDELAY, 0622, 0)) != NULL)
goto Found;
#endif
#ifdef PTYS_ARE_GETPTY
while ((ptydev = getpty()) != NULL)
if ((fd = open(ptydev, O_RDWR)) >= 0) {
ttydev = ptydev;
goto Found;
}
#endif
#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT)
# if defined(PTYS_ARE_GETPT) || defined(PTYS_ARE_PTMX)
{
/*@i@*/ extern char *ptsname();
# ifdef PTYS_ARE_GETPT
/*@-unrecog@*/
if ((fd = getpt()) >= 0) {
/*@+unrecog@*/
# else
if ((fd = open("/dev/ptmx", O_RDWR)) >= 0) {
# endif
if (grantpt(fd) == 0 /* change slave permissions */
&& unlockpt(fd) == 0) { /* slave now unlocked */
ptydev = ttydev = ptsname(fd); /* get slave's name */
changettyowner = 0;
goto Found;
}
close(fd);
}
}
# endif
#endif
#ifdef PTYS_ARE_PTC
if ((fd = open("/dev/ptc", O_RDWR)) >= 0) {
ptydev = ttydev = ttyname(fd);
goto Found;
}
#endif
#ifdef PTYS_ARE_CLONE
if ((fd = open("/dev/ptym/clone", O_RDWR)) >= 0) {
ptydev = ttydev = ptsname(fd);
goto Found;
}
#endif
#ifdef PTYS_ARE_NUMERIC
{
int idx;
char *c1, *c2;
char pty_name[] = "/dev/ptyp???";
char tty_name[] = "/dev/ttyp???";
ptydev = pty_name;
ttydev = tty_name;
c1 = &(pty_name[sizeof(pty_name) - 4]);
c2 = &(tty_name[sizeof(tty_name) - 4]);
for (idx = 0; idx < 256; idx++) {
sprintf(c1, "%d", idx);
sprintf(c2, "%d", idx);
if (access(ttydev, F_OK) < 0) {
idx = 256;
break;
}
if ((fd = open(ptydev, O_RDWR)) >= 0) {
if (access(ttydev, R_OK | W_OK) == 0) {
ttydev = strdup(tty_name);
goto Found;
}
close(fd);
}
}
}
#endif
#ifdef PTYS_ARE_SEARCHED
{
size_t len;
const char *c1, *c2;
char pty_name[] = "/dev/pty??";
char tty_name[] = "/dev/tty??";
len = sizeof(pty_name) - 3;
ptydev = pty_name;
ttydev = tty_name;
# define PTYCHAR1 "pqrstuvwxyz"
# define PTYCHAR2 "0123456789abcdef"
for (c1 = PTYCHAR1; *c1; c1++) {
ptydev[len] = ttydev[len] = *c1;
for (c2 = PTYCHAR2; *c2; c2++) {
ptydev[len + 1] = ttydev[len + 1] = *c2;
fd = open(ptydev, O_RDWR);
/* fprintf(stderr, "%s %s %d\n", ttydev, ptydev, fd); */
if (fd >= 0) {
if (access(ttydev, R_OK | W_OK) == 0) {
ttydev = strdup(tty_name);
goto Found;
}
close(fd);
}
}
}
}
#endif
print_dbg("can't open pseudo-tty");
return -1;
Found:
fcntl(fd, F_SETFL, O_NDELAY);
return fd;
}
/*}}} */
/*{{{ establish a controlling teletype for new session */
/*
* On some systems this can be done with ioctl() but on others we
* need to re-open the slave tty.
*/
/* INTPROTO */
static int
get_tty(void)
{
int fd, i;
pid_t pid;
/*
* setsid() [or setpgrp] must be before open of the terminal,
* otherwise there is no controlling terminal (Solaris 2.4, HP-UX 9)
*/
#ifndef ultrix
# ifdef NO_SETSID
pid = setpgrp(0, 0);
# else
pid = setsid();
# endif
if (pid < 0)
perror("get_tty: setsid: ");
# ifdef DEBUG_TTYMODE
print_dbg("(%s: line %d): PID = %d\n", __FILE__, __LINE__, pid);
# endif
#endif /* ultrix */
if ((fd = open(ttydev, O_RDWR)) < 0
&& (fd = open("/dev/tty", O_RDWR)) < 0) {
print_dbg("can't open slave tty %s", ttydev);
exit(EXIT_FAILURE);
}
#ifdef PTYS_ARE_PTMX
/*
* Push STREAMS modules:
* ptem: pseudo-terminal hardware emulation module.
* ldterm: standard terminal line discipline.
* ttcompat: V7, 4BSD and XENIX STREAMS compatibility module.
*/
if (!changettyowner) {
ioctl(fd, I_PUSH, "ptem");
ioctl(fd, I_PUSH, "ldterm");
#if !defined(sgi) && !defined(__sgi)
ioctl(fd, I_PUSH, "ttcompat");
#endif
}
#endif
if (changettyowner) {
/* change ownership of tty to real uid and real group */
unsigned int mode = 0622;
gid_t gid = getgid();
#ifdef TTY_GID_SUPPORT
{
struct group *gr = getgrnam("tty");
if (gr) {
/* change ownership of tty to real uid, "tty" gid */
gid = gr->gr_gid;
mode = 0620;
}
}
#endif /* TTY_GID_SUPPORT */
#ifndef __CYGWIN32__
{
privileges(RESTORE);
i = fchown(fd, getuid(), gid);
/*
fprintf(stderr, "fchown fd=%d uid=%d, gid=%d, res=%d\n",
fd, (int) getuid(), (int) gid, i);
*/
i = fchmod(fd, mode);
/*
fprintf(stderr, "fchmod fd=%d res=%d\n", fd, i);
*/
privileges(IGNORE);
}
#endif
}
/*
* Close all file descriptors. If only stdin/out/err are closed,
* child processes remain alive upon deletion of the window.
*/
/*
for (i = 3; i < 256; i++)
if (i != fd)
close(i);
*/
/* Reopen stdin, stdout and stderr over the tty file descriptor */
dup2(fd, 0); /* stdin */
#ifdef OPEN_SSH_D
dup2(fd, 1); /* stdout */ /* TRY */
dup2(fd, 2); /* stderr */ /* TRY */
#endif
if (fd > 2)
close(fd);
#ifdef ultrix
if ((fd = open("/dev/tty", O_RDONLY)) >= 0) {
ioctl(fd, /*@i@*/ TIOCNOTTY, 0);
close(fd);
} else {
pid = setpgrp(0, 0);
if (pid < 0)
perror("get_tty: setpgrp: ");
}
/* no error, we could run with no tty to begin with */
#else /* ultrix */
# ifdef TIOCSCTTY
ioctl(0, /*@i@*/ TIOCSCTTY, 0);
# endif
/* set process group */
# if defined (_POSIX_VERSION) || defined (__svr4__)
tcsetpgrp(0, pid);
# elif defined (TIOCSPGRP)
ioctl(0, /*@i@*/ TIOCSPGRP, &pid);
# endif
/* svr4 problems: reports no tty, no job control */
/* # if !defined (__svr4__) && defined (TIOCSPGRP) */
close(open(ttydev, O_RDWR, 0));
/* # endif */
#endif /* ultrix */
return fd;
}
/*}}} */
static void restore_tty (char * slave)
{
/* fail silently if unprivileged
*/
privileges (RESTORE);
chmod(slave, ttyfd_stat.st_mode);
chown(slave, ttyfd_stat.st_uid, ttyfd_stat.st_gid);
privileges (IGNORE);
return;
}
static int errflag = 0;
/* Read the ssh subprocess response. Braindead ssh expects password
* from /dev/tty, and does not fallback on stdin.
* Even more braindead: OpenSSH **PRINTS** the password prompt to /dev/tty !!
*/
static char * fdgets (char * buf, int size, int fd, int fderr
#ifdef OPEN_SSH
, int fdmaster
#endif
)
{
fd_set my_set;
struct timeval tv;
char * p;
int countbytes;
ssize_t count;
int select_now;
int num_sel;
int num_err;
int status;
long exstat;
int maxnum;
errflag = 0;
countbytes = 0;
select_now = 0;
tv.tv_sec = 1;
tv.tv_usec = 0;
buf[0] = '\0';
p = buf;
if (fd > fderr)
maxnum = fd;
else
maxnum = fderr;
#ifdef OPEN_SSH
if (maxnum > fdmaster)
maxnum = maxnum;
else
maxnum = fdmaster;
#endif
while ( countbytes < (size - 2)) {
FD_ZERO(&my_set);
FD_SET(fd, &my_set);
FD_SET(fderr, &my_set);
#ifdef OPEN_SSH
FD_SET(fdmaster, &my_set);
#endif
if ( (num_sel = select (maxnum+1, &my_set, NULL, NULL, &tv)) == -1)
{
if ( errno == EINTR ) /* try again */
continue;
num_err = errno;
fprintf (stderr, "Error in select(): %s\n", strerror(num_err));
buf[countbytes] = '\0';
return NULL;
}
/* on Linux, timeout is modified
*/
tv.tv_sec = 1;
tv.tv_usec = 0;
#ifdef DEBUG
fprintf (stderr, "SE: %d selected.\n", num_sel);
#endif
if (num_sel == 0) /* timeout */
{
if (NULL != strstr (buf, "password: ") )
{
buf[countbytes] = '\0';
return buf;
}
if (NULL != strstr (buf, "Enter passphrase") )
{
buf[countbytes] = '\0';
return buf;
}
if (NULL != strstr (buf, "(yes/no)? ") )
{
buf[countbytes] = '\0';
return buf;
}
++select_now;
if ( select_now > MAX_TIMEOUT )
{
fprintf (stderr, "Timeout after %d seconds.\n", MAX_TIMEOUT);
buf[countbytes] = '\0';
if (countbytes == 0)
return NULL;
else
return buf;
}
}
if ( FD_ISSET (fd, &my_set))
{
count = read (fd, p, 1); /* --- READ --- */
if (count > 0)
{
countbytes += count;
#ifdef DEBUG
fprintf (stderr, "S-: |%c|\n", p);
#endif
if (*p == '\n') /* newline, return */
{
buf[countbytes] = '\0';
return buf;
}
p += count; /* move buffer pointer forward */
FD_SET( fd, &my_set );
}
else if (count < 0)
{
num_err = errno;
if (0 < (exstat = (long) waitpid (child_pid,
&status, WNOHANG|WUNTRACED)))
{
/* Child has exited. Check exit value.
*/
child_pid = 0;
if (WIFEXITED(status) != 0 && 0 != WEXITSTATUS(status))
fprintf (stderr, "return code: %d\n",
WEXITSTATUS(status));
}
else
{
fprintf (stderr,
"Error reading from pipe: %s\n",
strerror(num_err));
}
buf[countbytes] = '\0';
return NULL;
}
else /* count == 0; broken pipe -> */
{
num_err = errno;
buf[countbytes] = '\0';
/* Child has exited. Check exit value.
*/
if (0 < waitpid (child_pid, &status, WNOHANG|WUNTRACED))
{
if (WIFEXITED(status) != 0 && 0 != WEXITSTATUS(status))
fprintf (stderr, "return code: %d\n",
WEXITSTATUS(status));
}
child_pid = 0;
return NULL;
}
}
if ( FD_ISSET (fderr, &my_set)
#ifdef OPEN_SSH
|| FD_ISSET (fdmaster, &my_set)
#endif
)
{
#ifdef OPEN_SSH
if (FD_ISSET (fdmaster, &my_set))
count = read (fdmaster, p, 1);
else
#endif
count = read (fderr, p, 1); /* --- READ --- */
#ifdef DEBUG
fprintf (stderr, "E-: |%c|\n", p);
#endif
if (count > 0)
{
countbytes += count;
if (*p == '\n') /* newline, return */
{
buf[countbytes] = '\0';
errflag = 1;
return buf;
}
p += count; /* move buffer pointer forward */
FD_SET( fderr, &my_set );
}
else if (count < 0)
{
num_err = errno;
/* Child has exited. Check exit value.
*/
if (0 < (exstat = (long) waitpid (child_pid,
&status, WNOHANG|WUNTRACED)))
{
if (WIFEXITED(status) != 0 && 0 != WEXITSTATUS(status))
fprintf (stderr, "return code: %d\n",
WEXITSTATUS(status));
}
else
{
fprintf (stderr,
"Error reading from pipe: %s\n",
strerror(num_err));
}
buf[countbytes] = '\0';
errflag = 1;
return NULL;
}
else /* count == 0; broken pipe -> */
{
; /* do nothing */
}
}
}
buf[countbytes] = '\0';
return buf;
}
static int user_handler(int mode, int master,
int input_pipe[], int output_pipe[],
int inerr_pipe[])
{
ssize_t rc; /* return values of functions. */
char line[256];
char * p;
char c;
int i = 0;
/* first, close unnecessary file descriptors
*/
close ( input_pipe[1]); /* we don't need to write to this pipe. */
close ( inerr_pipe[1]); /* we don't need to write to this pipe. */
close (output_pipe[0]); /* we don't need to read from this pipe. */
fcntl (input_pipe[0], F_SETFL, O_NONBLOCK);
fcntl (inerr_pipe[0], F_SETFL, O_NONBLOCK);
/* loop: read input, send via one pipe, read via other */
/* pipe, and write to stdout. exit on EOF from user. */
while ( NULL != fdgets (line, 256, input_pipe[0], inerr_pipe[0]
#ifdef OPEN_SSH
, master
#endif
) )
{
#ifdef DEBUG
print_dbg ("line in: [%d] <%s> \n", strlen(line), line);
#endif
if (NULL != strstr (line, "password: ") )
{
/* 'master' is the controlling terminal for
* the spawned subprocess that runs ssh
*/
if (password[0] == '\0')
{
get_pw();
}
rc = write(master, password, strlen(password));
rc += write(master, "\n", 1);
if (rc <= -1)
{
perror("user_handler: write");
close(input_pipe[0]);
close(output_pipe[1]);
memset (password, 0, 128);
memset (passphrase, 0, 128);
return -2;
}
print_dbg ("Password sent: %d bytes.", rc-1);
continue;
}
if (NULL != strstr (line, "Enter passphrase") )
{
/* 'master' is the controlling terminal for
* the spawned subprocess that runs ssh
*/
if (passphrase[0] == '\0')
{
get_pp(line);
}
rc = write(master, passphrase, strlen(passphrase));
rc = write(master, "\n", 1);
if (rc == -1)
{
perror("user_handler: write");
close(input_pipe[0]);
close(output_pipe[1]);
memset (password, 0, 128);
memset (passphrase, 0, 128);
return -2;
}
print_dbg ("Using passphrase to unlock private key.");
continue;
}
if (NULL != strstr (line, "(yes/no)? ") )
{
/* 'master' is the controlling terminal for
* the spawned subprocess that runs ssh
*/
rc = write(master, "yes", 3);
rc = write(master, "\n", 1);
if (rc == -1)
{
perror("user_handler: write");
close(input_pipe[0]);
close(output_pipe[1]);
memset (password, 0, 128);
memset (passphrase, 0, 128);
return -2;
}
print_dbg ("Login confirmation sent.");
continue;
}
if (errflag == 0 && mode == 0)
{
strncpy(hom_str, line, 256);
hom_str[255] = '\0';
p = &hom_str[strlen(hom_str)-1];
while (*p == ' ' || *p == '\n') *p = '\0';
print_dbg ("Received path of home directory <%s>", hom_str);
close(input_pipe[0]);
close(inerr_pipe[0]);
close(output_pipe[1]);
close(master);
return 0;
}
if (errflag == 1 && line[0] != '\n')
{
#ifdef OPEN_SSH
if (strlen(line) == 2)
continue;
#endif
if (line[strlen(line)-1] == '\n')
fprintf(stderr, "%s", line);
else
fprintf(stderr, "%s\n", line);
/* Permission denied.
*/
if (NULL != strstr (line, "Permission denied"))
{
memset (password, 0, 128);
memset (passphrase, 0, 128);
exit (EXIT_FAILURE);
}
/* stty thingy
*/
if (NULL != strstr (line, "stty:"))
continue;
/* more fun
*/
if (NULL != strstr (line, "Trying"))
continue;
/* more fun
*/
if (NULL != strstr (line, "Logging in"))
continue;
/* bad passphrase
*/
if (NULL != strstr (line, "Bad passphrase"))
continue;
/* new seed
*/
if (NULL != strstr (line, "Creating random seed file"))
continue;
/* blurb
*/
if (NULL != strstr (line, "Received signal"))
continue;
/* blurb
*/
if (NULL != strstr (line, "OpenSSH") &&
NULL != strstr (line, "SSH protocols"))
continue;
/* new host
*/
if (NULL != strstr (line, "list of known hosts"))
continue;
/* tar
*/
if (NULL != strstr (line, "tar: B: unknown option"))
{
opt_tarflag = FALSE;
close(input_pipe[0]);
close(inerr_pipe[0]);
close(output_pipe[1]);
close(master);
return -1;
}
/* some other error
*/
fprintf(stderr, "Continue (Y/n) ? ");
while (1 == 1)
{
c = getc(stdin);
if (c == 'Y' || c == 'y' || c == '\n')
break;
else if (c == 'N' || c == 'n') {
close(input_pipe[0]);
close(inerr_pipe[0]);
close(output_pipe[1]);
close(master);
return -2;
}
else {
fprintf(stderr, "Continue (Y/n) ? ");
}
}
continue;
}
if (errflag == 0 && mode == 1)
{
p = &line[strlen(line)-1];
while (*p) { if (*p == '\n') *p = '\0'; --p; }
if (line[strlen(line)-1] == ':')
{ printf("\n\n%s\n", line); }
else if (strlen(line) > 40)
{ printf("%s\n", line); }
else
{
printf("%s", line);
if (strlen(line) < 8)
printf("\t\t\t");
else if (strlen(line) < 16)
printf("\t\t");
else
printf("\t");
++i;
if (i == 3) { fputc('\n', stdout); i = 0; }
}
continue;
}
/* default
*/
if (errflag == 0)
fprintf(stdout, "%s", line);
}
if (mode == 1)
fputc('\n', stdout);
/* close pipes and exit.
*/
close(input_pipe[0]);
close(inerr_pipe[0]);
close(output_pipe[1]);
close(master);
return 0;
}
/* now comes the function executed by the translator process. */
static void translator(char * command, int input_pipe[], int output_pipe[],
int outerr_pipe[])
{
#ifdef DEBUG_P
char blah[256];
#endif
/* first, close unnecessary file descriptors
*/
close ( input_pipe[1]); /* we don't need to write to this pipe. */
close (output_pipe[0]); /* we don't need to read from this pipe. */
close (outerr_pipe[0]); /* we don't need to read from this pipe. */
fcntl (input_pipe[0], F_SETFL, O_NONBLOCK);
dup2(outerr_pipe[1], STDERR_FILENO );
dup2(output_pipe[1], STDOUT_FILENO );
#ifdef OPEN_SSH_D
dup2( input_pipe[0], STDIN_FILENO ); /* TRY */
#endif
#ifdef DEBUG_P
fputs ("password: ", stdout);
fflush (stdout);
fputs (fgets(blah, 255, stdin), stdout);
#endif
execl("/bin/sh", "sh", "-c", command, NULL);
fflush(stdout); sleep(1);
_exit (127);
}
/* spawn off the subprocess to run ssh
*/
int piper (char * command, int mode)
{
int master;
int slave; /* slave pty */
int retval = 0;
/* 2 arrays to contain file descriptors, for two pipes.
*/
int user_to_translator[2];
int translator_to_user[2];
int err_to_user[2];
pid_t pid; /* pid of child process, or 0 in parent thread. */
int rc;
if (command == NULL)
{
fprintf(stderr, "** ERROR **: No command given !");
return EXIT_FAILURE;
}
if ((master = get_pty()) < 0) {
perror("ptypair: could not open master pty");
exit (EXIT_FAILURE);
}
lstat(ttydev, &ttyfd_stat);
/* first, create one pipe.
*/
rc = pipe(user_to_translator);
if (rc == -1) {
perror("main: pipe user_to_translator");
exit (EXIT_FAILURE);
}
/* then, create another pipe.
*/
rc = pipe(translator_to_user);
if (rc == -1) {
perror("main: pipe translator_to_user");
exit (EXIT_FAILURE);
}
/* then, create yet another pipe.
*/
rc = pipe(err_to_user);
if (rc == -1) {
perror("main: pipe err_to_user");
exit (EXIT_FAILURE);
}
/* now fork off a child process, and set their handling routines. */
pid = fork();
switch (pid) {
case -1: /* fork failed. */
perror("main: fork");
memset (password, 0, 128);
memset (passphrase, 0, 128);
exit (EXIT_FAILURE);
case 0: /* child process. */
/* page lock is not inherited after
* the fork
*/
memset (password, 0, 128);
memset (passphrase, 0, 128);
/* We are in the child process */
/****************************************
close(master);
if (setsid() < 0) {
perror("could not set session leader");
}
if ((slave = get_slave_pty(name)) < 0) {
perror("ptypair: could not open slave tty");
fflush(stdout);
_exit (EXIT_FAILURE);
}
#ifdef TIOCSCTTY
if (ioctl(slave, TIOCSCTTY, NULL)) {
perror("could not set new controlling tty");
}
#endif
dup2(slave, STDIN_FILENO);
dup2(slave, 1);
dup2(slave, 2);
if (slave > 2)
close(slave);
*************************************/
/* establish a controlling teletype for the new session */
slave = get_tty();
close(master);
translator (command,
user_to_translator,
translator_to_user,
err_to_user);
break;
default: /* parent process. */
child_pid = pid;
retval = user_handler (mode, master,
translator_to_user,
user_to_translator,
err_to_user);
break;
}
/* just to be sure
*/
(void) waitpid (-1, NULL, WNOHANG|WUNTRACED);
child_pid = 0;
close(master);
restore_tty(ttydev);
return retval;
}
syntax highlighted by Code2HTML, v. 0.9.1