#!/usr/bin/perl -wT
# ssh-faker - Protect sshd from worms and hackers
# Copyright (C) 2005  Charles Howes <sshfaker@ch.pkts.ca>
# 
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

# Version 1.0  - 2005-07-16
# Version 1.1  - 2005-07-19  Move password test before url-escape

use strict;
use Socket;

my $password="password";   # CHANGE ME!!!!

#=================================================================

BEGIN {
  $ENV{'PATH'}="/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin";
  $|=1;
  open(STDERR, ">&STDOUT");
  $SIG{__DIE__}=\&mydie;
}
sub mydie {
  my $a=$_[0];
  syslog($a);
  print $a;
  exit 0;
}

if ($password eq "password") {die("Change the default password!\n");}

my $when=localtime;

# Buffer-overflowable versions of sshd:
my @bad=qw(
  SSH-1.5-1.2.23
  SSH-1.5-1.2.24
  SSH-1.5-1.2.25
  SSH-1.5-1.2.26
  SSH-1.5-1.2.27
  SSH-1.5-1.2.28
  SSH-1.5-1.2.29
  SSH-1.5-1.2.30
  SSH-1.5-1.2.31
  SSH-1.99-OpenSSH_2.2.0p1
);

my $ver;
#$ver=$bad[int(rand(@bad))];
#$ver="SSH-1.5-1.2.27";
$ver="SSH-1.99-OpenSSH_3.7.1p1";  # Don't confuse the hacker.

my $hersockaddr;
my $port;
my $iaddr;
my $herstraddr;

$herstraddr="console";
if (!defined $ARGV[0]) {
  die("Error: hosts.deny should contain:\n".
      "  sshd : ALL : twist /usr/local/bin/ssh-faker %a\n".
      "I can tell it doesn't because there was no command line argument.\n");
}
if ($ARGV[0]!~m/^([0-9a-f:.]{7,60})$/i) {
  die("$ARGV[0] isn't an address");
}
$herstraddr=$1;
if ($herstraddr=~m/^::ffff:([0-9.]{7,15})$/) { $herstraddr=$1; }

# This used to work, until ipv6 came along:

#$hersockaddr=getpeername(STDIN);
#if (defined $hersockaddr) {
#  ($port,$iaddr)=sockaddr_in($hersockaddr);
#  $herstraddr=inet_ntoa($iaddr);
#}

my $a=undef;
eval {
  print "$ver\r\n";
  local $SIG{ALRM}=sub {die("alarm");};
  alarm(60);
  $a=<STDIN>;
};

if (!defined $a) {$a="-timeout-";}

if ($a =~ m/$password/i) {
  if ($herstraddr eq "console") {
    open(OUT,">-");
  } else {
    open(OUT,">>/etc/hosts.allow") or die("/etc/hosts.allow: $!");
  }
  print OUT "# Added at $when\n";
  print OUT  "sshd: $herstraddr : ALLOW\n";
  close(OUT);
  print "Success!  You can now ssh to this computer.\n";
  exit 0;
}

$a=~s/[^a-zA-Z0-9._ -]/sprintf("%%%02.2x",ord($&))/ge;
$a=~s/ /+/g;

syslog("Got \"$a\" from $herstraddr"); # Don't log the password

# What we have here is a failure to communicate....
if ($a =~ m/^SSH-(\d+\.\d+)-(\S+)/) {
  print "\x00\x00\x00\x61";  # length of packet
  print "\x00\x00\x00\x00";  # dunno
  print "\x00\x00\x00\x01";  # opcode
  print "\x00\x00\x00\x58";  # length of text:
  print "Your ssh version is too old and is no longer supported.  Please install a newer version.";
  print "\x4F\x9D\xE0\xF2";  # CRC32 checksum
} else {
  print "Protocol mismatch.\n";
}

sub syslog {
  my $b=$_[0];
  open(OUT,"|logger -i -p user.notice -t sshd") or die("logger: $!");
  print OUT $b;
  close(OUT);
}
