My first SSH client attempt fails for me

Discussion of open issues, suggestions and bugs regarding network security and data protection solution - SecureBridge
Post Reply
sebastiaanv
Posts: 3
Joined: Sun 14 Apr 2019 13:30

My first SSH client attempt fails for me

Post by sebastiaanv » Sun 14 Apr 2019 13:56

Hello all,

Just started evaluating your secure bridge libraries. Please note i am a novice :) I am probably doing something wrong. Hopefully you can help me spot the error. I made a first attempt in creating a ssh client by gathering some example code from this forum. The SSH connection should execute a remote linux command and provide me the output back.

Some technical details:

Trying this on openSUSE Leap 15 with Free Pascal Compiler version 3.0.4 [2018/05/13] for x86_64. SSH attempts were made to openSUSE and Ubuntu VM, but both fail.

Attempt output openSUSE:

Exception at 0000000000548384: Exception:
You are using SecureBridge Trial edition!
You may register SecureBridge at http://www.devart.com.
SSH client connected!
CMD shell connected!

.... Note: From here takes some time (~10 seconds), waiting on timeout i guess but then follows another exception ...

An unhandled exception occurred at $0000000000424C8A:
EInOutError: Invalid file handle
$0000000000424C8A

On openSUSE vmware machine in journal log:

Apr 14 15:41:27 cereb-dev sshd[15686]: error: ioctl(TIOCSCTTY): Input/output error
Apr 14 15:41:27 cereb-dev sshd[15686]: error: /dev/pts/2: No such file or directory
Apr 14 15:41:27 cereb-dev sshd[15686]: error: open /dev/tty failed - could not set controlling tty: No such device or address

On Ubuntu similar error.

The source code i use.

Code: Select all

program ssh_test;

uses {$ifdef unix} cthreads, cmem, {$endif} 
  SysUtils,
  Classes,
  ScSSHUtils,
  ScTypes,
  ScUtils,
  ScBridge,
  ScSSHChannel,
  ScSSHClient;

type
  TMyClass = class
  class procedure ScSSHClientServerKeyValidate(Sender: TObject;
    NewServerKey: TScKey; var Accept: Boolean);
  end;

var
  searchResult: TSearchRec;
  ScSSHClient: TScSSHClient;
  ScFileStorage: TScFileStorage;
  ScSSHChannel : TScSSHChannel;  
  CMDshell : TScSSHShell;

class procedure TMyClass.ScSSHClientServerKeyValidate(Sender: TObject;
  NewServerKey: TScKey; var Accept: Boolean);
var
  Key: TScKey;
  fp, msg: string;
begin
  Key := ScFileStorage.Keys.FindKey(ScSSHClient.HostName);
  if (Key = nil) or not Key.Ready then begin
    NewServerKey.GetFingerPrint(haMD5, fp);
    Key := TScKey.Create(nil);
    try
      Key.Assign(NewServerKey);
      Key.KeyName := ScSSHClient.HostName;
      ScFileStorage.Keys.Add(Key);
    except
      Key.Free;
      raise;
    end;

    Accept := True;
  end;
end;

begin
  try
    ScFileStorage := TScFileStorage.Create(nil);
    ScSSHClient := TScSSHClient.Create(nil);
    CMDShell := TScSSHShell.Create(nil);
    ScSSHClient.KeyStorage := ScFileStorage;
    ScSSHClient.HostName := '<ip address or name>';
    ScSSHClient.User := '<username>';
    ScSSHClient.Password := '<password>';
    ScSSHClient.OnServerKeyValidate := TMyClass.ScSSHClientServerKeyValidate;     

    ScSSHClient.Connect;
    if (ScSSHClient.connected) then begin
     writeln('SSH client connected!');
    end
    else begin
     writeln('SSH client failed!');
    end;

    CMDShell.Client := ScSSHClient;
    CMDShell.Connect;
    if (CMDShell.Connected) then begin
     writeln('CMD shell connected!');
    end
    else begin
     writeln('CMD shell failed!');
    end;

    CMDShell.ExecuteCommand('uname -a');

  except
    on E: Exception do
      writeln(E.ClassName, ': ', E.Message);
  end;

  ScSSHClient.DisConnect;
  ScSSHClient.Destroy;
  ScSSHClient.Free;
end.
Hope you can help me out, thanks in advance!

sebastiaanv
Posts: 3
Joined: Sun 14 Apr 2019 13:30

Re: My first SSH client attempt fails for me

Post by sebastiaanv » Sun 14 Apr 2019 18:18

Ah ... never mind. I figured out the exceptions and most of all why i did not receive the executed result.

Below works for me (sorry for the all lower case code).

Code: Select all

program ssh_client_devart;

uses {$ifdef unix} cthreads, cmem, {$endif} sysutils, classes, ScSSHUtils, ScTypes, ScUtils, ScBridge, ScSSHChannel, ScSSHClient; 

type
  tmyclass = class
  class procedure scsshclientserverkeyvalidate(sender: tobject;
    newserverkey: tsckey; var accept: boolean);
  end;

var filestorage: tscfilestorage;
    cmd_shell: tscsshshell;
    ssh_client: tscsshclient;
    ssh_channel: tscsshchannel;

 class procedure tmyclass.scsshclientserverkeyvalidate(sender: tobject; newserverkey: tsckey; var accept: boolean);

 var key: tsckey;
     fp: string;

 begin
  key := filestorage.keys.findkey(ssh_client.hostname);
  if (key = nil) or not key.ready then begin
   newserverkey.getfingerprint(hamd5, fp);
   key := tsckey.create(nil);
   try
    key.assign(newserverkey);
    key.keyname := ssh_client.hostname;
    filestorage.keys.add(key);
   except
    key.free;
    raise;
   end;
   accept := true;
  end;
 end;

begin

 try
  ssh_client := tscsshclient.create(nil);
  ssh_client.hostname := '<ip address>';
  ssh_client.user := '<username>';
  ssh_client.password := 'password';
  ssh_client.authentication := atpassword;
  filestorage := tscfilestorage.create(nil);                                                                                                                                                                                                                                                                                  ssh_client.keystorage := filestorage;
  ssh_client.onserverkeyvalidate := tmyclass.scsshclientserverkeyvalidate;
 except on e: exception do
  writeln('Exception error occured: ', e.classname, ': ', e.message);
 end; 

 try
  ssh_client.connect;
  if (ssh_client.connected) then begin
     writeln('ssh client connected!');
  end
  else begin
   writeln('ssh client failed!');
  end;

  cmd_shell := tscsshshell.create(nil);
  cmd_shell.client := ssh_client;
  cmd_shell.nonblocking := false;
  writeln(cmd_shell.executecommand('uname -a'));

 except on e: exception do
  writeln('Exception error occured: ', e.classname, ': ', e.message);
 end;

 ssh_client.disconnect;
end.
I noticed also there is another method: async. Does anyone maybe have an example for me how this works?

ViktorV
Devart Team
Posts: 2691
Joined: Wed 30 Jul 2014 07:16

Re: My first SSH client attempt fails for me

Post by ViktorV » Mon 15 Apr 2019 11:12

sebastiaanv wrote:
Sun 14 Apr 2019 18:18
Ah ... never mind. I figured out the exceptions and most of all why i did not receive the executed result.

Below works for me (sorry for the all lower case code).

Code: Select all

program ssh_client_devart;

uses {$ifdef unix} cthreads, cmem, {$endif} sysutils, classes, ScSSHUtils, ScTypes, ScUtils, ScBridge, ScSSHChannel, ScSSHClient; 

type
  tmyclass = class
  class procedure scsshclientserverkeyvalidate(sender: tobject;
    newserverkey: tsckey; var accept: boolean);
  end;

var filestorage: tscfilestorage;
    cmd_shell: tscsshshell;
    ssh_client: tscsshclient;
    ssh_channel: tscsshchannel;

 class procedure tmyclass.scsshclientserverkeyvalidate(sender: tobject; newserverkey: tsckey; var accept: boolean);

 var key: tsckey;
     fp: string;

 begin
  key := filestorage.keys.findkey(ssh_client.hostname);
  if (key = nil) or not key.ready then begin
   newserverkey.getfingerprint(hamd5, fp);
   key := tsckey.create(nil);
   try
    key.assign(newserverkey);
    key.keyname := ssh_client.hostname;
    filestorage.keys.add(key);
   except
    key.free;
    raise;
   end;
   accept := true;
  end;
 end;

begin

 try
  ssh_client := tscsshclient.create(nil);
  ssh_client.hostname := '<ip address>';
  ssh_client.user := '<username>';
  ssh_client.password := 'password';
  ssh_client.authentication := atpassword;
  filestorage := tscfilestorage.create(nil);                                                                                                                                                                                                                                                                                  ssh_client.keystorage := filestorage;
  ssh_client.onserverkeyvalidate := tmyclass.scsshclientserverkeyvalidate;
 except on e: exception do
  writeln('Exception error occured: ', e.classname, ': ', e.message);
 end; 

 try
  ssh_client.connect;
  if (ssh_client.connected) then begin
     writeln('ssh client connected!');
  end
  else begin
   writeln('ssh client failed!');
  end;

  cmd_shell := tscsshshell.create(nil);
  cmd_shell.client := ssh_client;
  cmd_shell.nonblocking := false;
  writeln(cmd_shell.executecommand('uname -a'));

 except on e: exception do
  writeln('Exception error occured: ', e.classname, ': ', e.message);
 end;

 ssh_client.disconnect;
end.
I noticed also there is another method: async. Does anyone maybe have an example for me how this works?
We do not understand the essence of your question.
In order for us to be able to give you a detailed answer, please provide a more detailed and clear description of your question.

sebastiaanv
Posts: 3
Joined: Sun 14 Apr 2019 13:30

Re: My first SSH client attempt fails for me

Post by sebastiaanv » Mon 15 Apr 2019 15:23

What i am now looking for is asynchronous communication. I think this is the term you use in the documentation. The method which i now figured out to use is what you call synchronous. I make an SSH connection to an Linux box and i am able to execute one command (ScSSHShell.ExecuteCommand). The response is sent, but also the connection is closed.

I am looking for a way to keep the connection open (is it nonblocking?). I want to send a command via SSH. Await response by keeping the connection open and send another command in response (depending on result sent back by Linux box). In short I want to keep the shell active until i tell it i am finished by sending some kind of disconnect.

I was hoping you can provide some example code for this. If this is also possible i will buy the securebridge license for myself.

ViktorV
Devart Team
Posts: 2691
Joined: Wed 30 Jul 2014 07:16

Re: My first SSH client attempt fails for me

Post by ViktorV » Tue 16 Apr 2019 07:15

The shell channel particularity is that after command executing, using the TScSSHShell.ExecuteCommand method, the channel will be closed by the server after receiving a command execution result, regardless of a value of the TScSSHShell.NonBlocking property and a client cannot affect it.
You can connect to the server and send commands to the server using the TScSSHShell.WriteString or TScSSHShell.WriteBuffer methods, after their execution the channel is not closed: https://www.devart.com/sbridge/docs/tsc ... string.htm, https://www.devart.com/sbridge/docs/tsc ... buffer.htm and read the result using the TScSSHShell.ReadString and TScSSHShell.ReadBuffer methods.
In NonBlocking mode after the TScSSHShell.WriteString (TScSSHShell.WriteBuffer) method execution the control is returned immediately and to get the result, you should handle the OnAsyncReceive event. When data is received from the server, the OnAsyncReceive event will arise.
Our SSHClientApp demo project demonstrating the use of the TScSSHShell component. You can find the SSHClientApp project in the% SecureBridgeDemos%\SSHClientApp directory. %SecureBridgeDemos% is the directory where SecureBridge Demo projects are installed on your computer.

Post Reply