Blame src/SSH_Access.cc

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