1
votes

I realize this question is asked frequently, mainly by people who want to intercept the password-asking phase of SSH. This is not what I want. I'm after the post-login text.

I want to write a wrapper for ssh, that acts as an intermediary between SSH and the terminal. I want this configuration:

(typing on keyboard / stdin) ----> (wrapper) ----> (ssh client)

and the same for output coming from ssh:

(ssh client) -----> (wrapper) -----> stdout

I seem to be able to attain the effect I want for stdout by doing a standard trick I found online (simplified code):

pipe(fd)
if (!fork()) {
  close(fd[READ_SIDE]);
  close(STDOUT_FILENO);  // close stdout ( fd #1 )
  dup(fd[WRITE_SIDE]);   // duplicate the writing side of my pipe ( to lowest # free pipe, 1 )
  close(STDERR_FILENO);
  dup(fd[WRITE_SIDE]);
  execv(argv[1], argv + 1); // run ssh
} else {
  close(fd[WRITE_SIDE]);
  output = fdopen(fd[READ_SIDE], "r");
  while ( (c = fgetc(output)) != EOF) {
    printf("%c", c);
    fflush(stdout);
  }
}

Like I said, I think this works. However, I can't seem to do the opposite. I can't close(STDIN_FILENO) and dup the readside of a pipe. It seems that SSH detects this and prevents it. I've read I can use the "-t -t" option to force SSH to ignore the non-stdin nature of its input; but when I try this it still doesn't work.

Any hints?

Thanks very much!

5
You should really be using dup2 rather than dup to replace stdout/stdin etc.Hasturkun
ssh shouldn't care about the filetype of stdin, if you use a private key instead of a password. Can you try that?Ben Voigt

5 Answers

1
votes

Use popen (instead of execv) to execute the ssh cmd and be able to read and write to the session.

1
votes

A pipe will not work if you want to allow any interactive use of ssh with the interceptor in place. In this case, you need to create a pseudo-tty. Look up the posix_openpt, ptsname, and grantpt functions. There's also a nonstandard but much-more-intuitive function called openpty, and a wrapper for it called forkpty, which make what you're trying to do extremely easy.

1
votes

Python's Paramiko does all of this with SSH but it is in Python source code. However, for a C programmer, reading Python is a lot like reading pseudocode so go to the source and learn exactly what works.

0
votes

Here's a working example that writes to ssh:

#include <unistd.h>

int main(int argc, char **argv)
{
    int pid;
    int fds[2];

    if (pipe(fds))
        return -1;

    pid = fork();

    if (!pid)
    {
        close(fds[1]);
        close(STDERR_FILENO);
        dup2(fds[0], STDIN_FILENO);
        execvp(argv[1], argv + 1);
    }
    else
    {
        char buf[256];
        int rc;

        close(fds[0]);
        while ((rc = read(STDIN_FILENO, buf, 256)) > 0)
        {
            write(fds[1], buf, rc);
        }
    }
    wait(NULL);
    return 0;
}
0
votes

This line is probably wrong:

  execv(argv[1], argv + 1); // run ssh

The array must be terminated by a NULL pointer, if you are using argv[] the parameter from main() I don't think there is any guarantee that this is the case. Edit: just checked the C99 standard and argv is NULL terminated.

execv() does not search the path for the file to execute, so if you are passing ssh as the parameter, it is equivalent to ./ssh which is probably not what you want. You could use execvp() but that is a security risk if a malicious program called ssh appears in $PATH before /bin/ssh. Better to use execv() and force the correct path.