|
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 |
}
|