Blame src/SSH_Access.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include "SSH_Access.h"
Packit 8f70b4
#include "misc.h"
Packit Service 4fd42a
#include <algorithm>
Packit Service 4fd42a
#include "ascii_ctype.h"
Packit 8f70b4
Packit 8f70b4
void SSH_Access::MakePtyBuffers()
Packit 8f70b4
{
Packit 8f70b4
   int fd=ssh->getfd();
Packit 8f70b4
   if(fd==-1)
Packit 8f70b4
      return;
Packit 8f70b4
   ssh->Kill(SIGCONT);
Packit 8f70b4
   send_buf=new IOBufferFDStream(new FDStream(ssh->getfd_pipe_out(),"pipe-out"),IOBuffer::PUT);
Packit 8f70b4
   recv_buf=new IOBufferFDStream(new FDStream(ssh->getfd_pipe_in(),"pipe-in"),IOBuffer::GET);
Packit 8f70b4
   pty_send_buf=new IOBufferFDStream(ssh.borrow(),IOBuffer::PUT);
Packit 8f70b4
   pty_recv_buf=new IOBufferFDStream(new FDStream(fd,"pseudo-tty"),IOBuffer::GET);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static bool ends_with(const char *b,const char *e,const char *suffix)
Packit 8f70b4
{
Packit 8f70b4
   int len=strlen(suffix);
Packit 8f70b4
   return (e-b>=len && !strncasecmp(e-len,suffix,len));
Packit 8f70b4
}
Packit 8f70b4
static bool begins_with(const char *b,const char *e,const char *suffix)
Packit 8f70b4
{
Packit 8f70b4
   int len=strlen(suffix);
Packit 8f70b4
   return (e-b>=len && !strncasecmp(b,suffix,len));
Packit 8f70b4
}
Packit 8f70b4
Packit Service 4fd42a
struct nocase_eq
Packit Service 4fd42a
{
Packit Service 4fd42a
   inline bool operator() (char lhs, char rhs) const
Packit Service 4fd42a
   {
Packit Service 4fd42a
      return c_tolower(lhs) == c_tolower(rhs);
Packit Service 4fd42a
   };
Packit Service 4fd42a
};
Packit Service 4fd42a
Packit Service 4fd42a
static bool contains(char const *begin, char const *end, char const *needle)
Packit Service 4fd42a
{
Packit Service 4fd42a
   return std::search(begin, end, needle, needle+strlen(needle), nocase_eq()) != end;
Packit Service 4fd42a
}
Packit Service 4fd42a
Packit Service 4fd42a
static bool IsConfirmPrompt(const char *b,const char *e)
Packit Service 4fd42a
{
Packit Service 4fd42a
   if(b==e)
Packit Service 4fd42a
      return false;
Packit Service 4fd42a
   return e[-1]=='?' && contains(b,e,"yes/no");
Packit Service 4fd42a
}
Packit Service 4fd42a
Packit 8f70b4
int SSH_Access::HandleSSHMessage()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
   pty_recv_buf->Get(&b,&s);
Packit 8f70b4
   const char *eol=find_char(b,s,'\n');
Packit 8f70b4
   if(!eol)
Packit 8f70b4
   {
Packit 8f70b4
      if(s>0 && b[s-1]==' ')
Packit 8f70b4
	 s--;
Packit 8f70b4
      if(ends_with(b,b+s,"password:")
Packit 8f70b4
      || (ends_with(b,b+s,"':") && s>10)
Packit 8f70b4
      || (begins_with(b,b+s,"password for ") && b[s-1]==':'))
Packit 8f70b4
      {
Packit 8f70b4
	 if(!pass)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(LOGIN_FAILED,_("Password required"));
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(password_sent>0)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(LOGIN_FAILED,_("Login incorrect"));
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 pty_recv_buf->Put("XXXX");
Packit 8f70b4
	 pty_send_buf->Put(pass);
Packit 8f70b4
	 pty_send_buf->Put("\n");
Packit 8f70b4
	 password_sent++;
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit Service 4fd42a
      if(IsConfirmPrompt(b,b+s))
Packit 8f70b4
      {
Packit 8f70b4
	 const char *answer=QueryBool("auto-confirm",hostname)?"yes\n":"no\n";
Packit 8f70b4
	 pty_recv_buf->Put(answer);
Packit 8f70b4
	 pty_send_buf->Put(answer);
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      if(!received_greeting && recv_buf->Size()>0)
Packit 8f70b4
      {
Packit 8f70b4
	 recv_buf->Get(&b,&s);
Packit 8f70b4
	 eol=find_char(b,s,'\n');
Packit 8f70b4
	 if(eol)
Packit 8f70b4
	 {
Packit 8f70b4
	    xstring &line=xstring::get_tmp(b,eol-b);
Packit 8f70b4
	    if(line.eq(greeting))
Packit 8f70b4
	       received_greeting=true;
Packit 8f70b4
	    LogRecv(4,line);
Packit 8f70b4
	    recv_buf->Skip(eol-b+1);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      LogSSHMessage();
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
   if(begins_with(b,b+s,"Host key verification failed"))
Packit 8f70b4
   {
Packit 8f70b4
      LogSSHMessage();
Packit 8f70b4
      SetError(FATAL,xstring::get_tmp(b,eol-b));
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(eol>b && eol[-1]=='\r')
Packit 8f70b4
      eol--;
Packit 8f70b4
   if(!hostname_valid) {
Packit 8f70b4
      if(ends_with(b,eol,"Name or service not known")
Packit 8f70b4
      || ends_with(b,eol,"No address associated with hostname"))
Packit 8f70b4
      {
Packit 8f70b4
	 LogSSHMessage();
Packit 8f70b4
	 SetError(LOOKUP_ERROR,xstring::get_tmp(b,eol-b));
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   LogSSHMessage();
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SSH_Access::LogSSHMessage()
Packit 8f70b4
{
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
   pty_recv_buf->Get(&b,&s);
Packit 8f70b4
   const char *eol=find_char(b,s,'\n');
Packit 8f70b4
   if(!eol)
Packit 8f70b4
   {
Packit 8f70b4
      if(pty_recv_buf->Eof())
Packit 8f70b4
      {
Packit 8f70b4
	 if(s>0)
Packit 8f70b4
	    LogRecv(4,b);
Packit 8f70b4
	 LogError(0,_("Peer closed connection"));
Packit 8f70b4
      }
Packit 8f70b4
      if(pty_recv_buf->Error())
Packit 8f70b4
	 LogError(4,"pty read: %s",pty_recv_buf->ErrorText());
Packit 8f70b4
      if(pty_recv_buf->Eof() || pty_recv_buf->Error()) {
Packit 8f70b4
	 if(last_ssh_message && time_t(now)-last_ssh_message_time<4)
Packit 8f70b4
	    LogError(0,"%s",last_ssh_message.get());
Packit 8f70b4
	 Disconnect(last_ssh_message);
Packit 8f70b4
      }
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   s=eol-b+1;
Packit 8f70b4
   int chomp_cr=(s>=2 && b[s-2]=='\r');
Packit 8f70b4
   last_ssh_message.nset(b,s-1-chomp_cr);
Packit 8f70b4
   last_ssh_message_time=now;
Packit 8f70b4
   pty_recv_buf->Skip(s);
Packit 8f70b4
   LogRecv(4,last_ssh_message);
Packit 8f70b4
   if(last_ssh_message.begins_with("ssh: "))
Packit 8f70b4
      last_ssh_message.set(last_ssh_message+5);
Packit 8f70b4
Packit 8f70b4
   if(!received_greeting && last_ssh_message.eq(greeting)) {
Packit 8f70b4
      received_greeting=true;
Packit 8f70b4
      hostname_valid=true;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SSH_Access::DisconnectLL()
Packit 8f70b4
{
Packit 8f70b4
   if(send_buf)
Packit 8f70b4
      LogNote(9,_("Disconnecting"));
Packit 8f70b4
   rate_limit=0;
Packit 8f70b4
   send_buf=0;
Packit 8f70b4
   recv_buf=0;
Packit 8f70b4
   pty_send_buf=0;
Packit 8f70b4
   pty_recv_buf=0;
Packit 8f70b4
   ssh=0;
Packit 8f70b4
   received_greeting=false;
Packit 8f70b4
   password_sent=0;
Packit 8f70b4
   last_ssh_message.unset();
Packit 8f70b4
   last_ssh_message_time=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SSH_Access::MoveConnectionHere(SSH_Access *o)
Packit 8f70b4
{
Packit 8f70b4
   send_buf=o->send_buf.borrow();
Packit 8f70b4
   recv_buf=o->recv_buf.borrow();
Packit 8f70b4
   pty_send_buf=o->pty_send_buf.borrow();
Packit 8f70b4
   pty_recv_buf=o->pty_recv_buf.borrow();
Packit 8f70b4
   ssh=o->ssh.borrow();
Packit 8f70b4
   received_greeting=o->received_greeting;
Packit 8f70b4
   hostname_valid|=o->hostname_valid;
Packit 8f70b4
   password_sent=o->password_sent;
Packit 8f70b4
   last_ssh_message.move_here(o->last_ssh_message);
Packit 8f70b4
   last_ssh_message_time=o->last_ssh_message_time; o->last_ssh_message_time=0;
Packit 8f70b4
}