#!/usr/local/bin/tops -s /usr/local/tops/sys -u /opt/mytops/usr/
{
   File tops_sshd
   August 2008

   Copyright (C) 2008  Dale R. Williamson

   The bastards never stop trying.

   Run a fake sshd server that sends the initial sshd id string and 
   then causes the remote ssh client to first wait and then close the 
   connection on a "bad packet length" error due to bytes it receives.

   Basic nmap shows fake port 22 as Service ssh:

      [dale@kaffia] /home/dale > nmap -sT xmysite.com

      Starting nmap V. 2.54BETA31 ( www.insecure.org/nmap/ )
      Interesting ports on xmysite.com (XX.YYY.Z.119):
      (The 1550 ports scanned but not shown below are in state: closed)
      Port       State       Service
      22/tcp     open        ssh                     
      80/tcp     open        http                    
      623/tcp    filtered    unknown                 
      664/tcp    filtered    unknown                 

      Nmap run completed -- 1 IP address (1 host up) scanned in 15 sec
      [dale@kaffia] /home/dale > 

   Here is a client attempting to connect and taking about 10 seconds 
   to fail:

      [dale@kaffia] /home/dale > time ssh admin@xmysite.com
      dead beef dead beef 
      Disconnecting: Bad packet length -559038737.

      real    0m10.365s
      user    0m0.020s
      sys     0m0.010s
      [dale@kaffia] /home/dale > 
}
\-----------------------------------------------------------------------

\  Words.

   CATMSG push no catmsg

   inline: SSH_ALLOW (qIP --- f) \ allow any IP to connect once only
      [ \ This file will contain IPs to deny:
          "HOME" env "tops_sshd.deny" catpath "FILE" book

        \ Bank file name into APP_CLIENT_ALLOW that will verify IPs:
          FILE "APP_CLIENT_ALLOW" "FILE" bank
      ]
    \ Run APP_CLIENT_ALLOW in sys/uboot.v:
      (qIP) dup APP_CLIENT_ALLOW (f)
      IF  
       \ Add IP to deny list in APP_CLIENT_ALLOW.FILE (known here
       \ locally as FILE):
         (qIP) spaced date +

         FILE file? IF FILE asciiload ELSE "" THEN (hT)

         (qIP hT) swap pile (hT1)
         (hT1) chop noblanklines neat (hT1) FILE save
         true (f)

      ELSE (qIP) drop false (f)
      THEN (f)
   end

\  Bank ptr to SSH_ALLOW into CLIENT_ALLOW.APP_CLIENT_ALLOW, so the 
\  system will run it to verify each connection:
   "SSH_ALLOW" ptr "CLIENT_ALLOW" "APP_CLIENT_ALLOW" bank

   inline: SSH_CLOSE ( --- )
\     Close all remote client sockets.
      remotesockets rows any?
      IF (rows) 1st
         DO " SSH_CLOSE: closing " 
            clientIPs remotesockets I pry (nS) dup push 
            (nS) clientindex quote strchop (qIP) + spaced date + . nl

            pull (nS) sclose
         LOOP
      THEN
   end

   inline: SSH_CONNECT (nSocket --- )
\     The ptr to this word is banked below as SERV_F.ADDptr, and it
\     runs on the earliest indication of a connection on Socket.

\     Note that clientmake() has not yet been run; for example, word 
\     clients will not show a client.

      [ \ This macro sends the initial sshd string to an ssh client, 
        \ causing it to start handshaking; responses from the client
        \ are ignored here since SERVE_F.SERVICE is set to 2drop (see
        \ below):
          "'SSH-2.0-OpenSSH_3.8.1p1 Debian 1:3.8.1p1-8' NLch + "
          "S remoteputf" + "SSH_ID" macro

          0 "TCON" book \ time of last connection

        \ Delay times (seconds):
          2 "TID" book \ must be less than WASTE
          5 "WASTE" book \ if too big, client disconnects
          10 "DT" book
          20 "WAIT" book
      ]
      "S" book
      " SSH_CONNECT: IP " clientIPs S clientindex quote strchop +
      " connecting " + date + . nl
{
      This is the earliest notification of a connection, and at this
      stage the client has not been completed, so trying to run macro 
      SSH_ID will produce an error in remoteputf: "invalid client 
      socket descriptor."

      This is a design issue that can be corrected (by making less
      rigorous the socket check in remoteputf, for example), but for 
      now just use an alarm to run macro SSH_ID in TID seconds after 
      enough time has passed for clientmake() to run:
}     TID (seconds) "SSH_CONNECT" "SSH_ID" localref ALARM

    \ Start another alarm to waste some time and then send bogus bytes;
    \ the bytes sent will cause the ssh client to close:
      WASTE (seconds) "SSH_SEND" ALARM

      tasks

    \ Flood control:
      time TCON - DT <
      IF " SSH_CONNECT: flood control; ignore new connections for "
         WAIT intstr + " seconds " + . nl WAIT SERVER_WAIT
      THEN
      time "TCON" book
   end

   inline: SSH_SEND ( --- )
\     This word sends junk bytes that cause ssh clients to close on
\     a bad packet length error.
      remotesockets rows any?
      IF (rows) 1st
         DO " SSH_SEND: bytes sent to " 
            clientIPs remotesockets I pry (nS) dup push 
            (nS) clientindex quote strchop (qIP) + spaced date + . nl

            "EFBEADDE" hexuint int4 dup + (hT) pull (nS) remoteputf
         LOOP
         10 "SSH_CLOSE" ALARM \ in 10 seconds, close anything still open

      THEN
   end

   inline: SSH_WAIT ( --- ) \ server ignore connections for a while
\     This needs to be checked out.  From a netstat view, SERVER_WAIT 
\     appears to allow connections to be ESTABLISHED during the WAIT
\     period.
      [ 60 "SEC" book ] 
      remotesockets any? 
      IF " SSH_WAIT: closing sockets: " over bend itext neat + . nl
         (hS) sclose 
      THEN
      SEC SERVER_WAIT 
   end
      
   pull catmsg

\-----------------------------------------------------------------------

\  Logging.
\  A log file is defined for SYSOUT using word set_sysout:
      "HOME" env "tops_sshd.log" catpath "LOG" book LOG set_sysout

\  Display some starting lines in the log file:
      "-" 72 cats nl dot nl
      "PID " getpid int$ cat spaced date cat dot nl

\-----------------------------------------------------------------------

   30 new_client_timeout

\  These lines set up the server type and ptrs to functions that 
\  respond to connection activity (see word SERVE_F in net.v):
   1 (type TERM) "SERVE_F" "TYPE" bank
   "SSH_CONNECT" ptr "SERVE_F" "ADDptr" bank \ respond to connection
   "2drop" ptr "SERVE_F" "SERVICE" bank \ drop bytes from ssh client 

   22 "PORT" book

\  1 "SSH_WAIT" "SEC" yank 2 * / "SSH_WAIT" PLAY

\  Start the server:
   "*" PORT DSERVER

   private halt

\-----------------------------------------------------------------------

   Appendix

   inline: SSH_ALLOW (qIP --- f) \ allow any IP to connect
      (qIP) drop true (f) 
   end

