Kermit File Transfer and Management as an SSH Subsystem

  Kermit as an SFTP Replacement

D R A F T

Most recent update: Tue Apr 16 17:19:26 2002

CONTENTS

[ C-Kermit ] [ C-Kermit Tutorial ] [ Kermit Scripts ] [ Kermit Home ]


Now that SSH connections are replacing Telnet and FTP at many sites, we frequently hear complaints about SFTP (and SCP), the main ones being:

Now with C-Kermit and Kermit 95, you can make Kermit client/server connections over SSH, thus combining the best of both worlds: SSH security plus Kermit's capabilities:

Each of these points is elaborated at some length at the Kermit website.


WHAT YOU NEED

[ Top ] [ Contents ] [ Next ]

On the Unix host:

On any Unix client:

On Windows:

On the host, install the C-Kermit 8.0 binary in the normal way. Then, in the same directory as the C-Kermit binary, make a symbolic link:

ln -s kermit kermit-sshsub

Then in the sshd configuration file, add a line:

Subsystem  kermit   /some/path/kermit-sshsub

(where /some/path is the fully specified directory where the symlink is.) This is similar to the line that sets up the SFTP subsystem. Example:

Subsystem   sftp    /usr/local/libexec/sftp-server
Subsystem   kermit  /usr/local/bin/kermit-sshsub

The mechanics might vary for other SSH servers; "man sshd" for details. The method shown here is used because the OpenSSH server does not permit the subsystem invocation to include command-line options. C-Kermit would have no way of knowing that it should enter Server mode if it were not called by a special name.

[ K95 Home ] [ C-Kermit Home ] [ OpenSSH Home ] [ Kermit Home ] [ C-Kermit Daily Updates ]


THE WINDOWS CLIENT

[ Top ] [ Contents ] [ Next ]

Kermit 95 1.1.21 and later have an SSH client built in, so making SSH connections to the Kermit subsystem is perfectly straightforward:

set host [ /user:username ] /network:ssh hostname /subsystem:kermit

You can omit the /USER: switch if your local and remote usernames are the same. If a password is required, you are prompted locally (or you can supply it in the SET HOST command; see the K95 SSH Client documentation for details). If the SET HOST command succeeds, you have a Kermit client/server connection over an SSH transport. At the K-95> prompt (or in a Kermit 95 script program) you can issue all the same commands you would normally use with a Kermit server: SEND, GET, REMOTE DIRECTORY, REMOTE DELETE, and so on. BYE or FINISH terminates the connection.

[ K95 Home ] [ K95 Tutorial ] [ K95 SSH Client ]


THE UNIX CLIENT

[ Top ] [ Contents ] [ Next ]

Since C-Kermit does not have SSH built in, it must use an external ssh client as the connection agent and transport. This is done through pipes, a capability that was added in C-Kermit 7.0 (January 2000). Conceptually it could also be done with pseudoterminals, but in practice there are some logistical roadblocks on each end.

NOTE: C-Kermit 8.0 has an SSH command, which makes a terminal connection to an SSH host. This is not the same thing as a client/server connection. C-Kermit's SSH command can NOT be used to access an SSH Kermit subsystem.

Since we are using external ssh client software, and since there are different ssh clients (and different releases of each one), the exact command to be used to make an SSH/Kermit connection can vary. Here is the command for the OpenSSH 3.0.2p1 client:

set host /pipe ssh -e none [ -l username ] -T -s hostname kermit

Example:

set host /pipe ssh -e none -l olga -T -s hq.xyzcorp.com kermit

The SSH client might or might not prompt you for a password or other information before it makes the connection; this depends on your SSH configuration (your public and private keys, your authorized hosts file, etc). Here's a brief synopsis of the OpenSSH client command syntax ("man ssh" for details):

-e none
This tells the SSH client to use no escape character. Since we will be transferring files across the connection, we don't want the connection to suddenly block because some character in the data.

-l username
This is the username on the remote host. You can omit the -l option and its argument if your local and remote usernames are the same. If they are different, you must supply the remote username.

-T
This tells the SSH client to tell the SSH server not to allocate a pseudoterminal. We are not making a terminal connection, we don't need a terminal, and in fact if a terminal were allocated on the remote end, the connection would not work.

-s ... kermit
This tells the SSH client to tell the SSH server to start the specified subsystem ("kermit") once the connection is made. The subsystem name comes after the hostname.

hostname
The IP host name or address of the desired host.

You might want to include other or additional ssh command-line options; "man ssh" explains what they are. Here are some examples for the OpenSSH 3.0.2p1 client:

-oClearAllForwardings yes
-oForwardAgent no
-oForwardX11 no
-oFallbackToRsh no
These ensure that a secure connection is used and that the connection used for file transfer is not also used for forwarding other things that might be specified in the ssh_config file.

-oProtocol 2
(i.e. SSH v2) Ensures that the negotiated protocol supports subsystems.

[ C-Kermit Home ] [ C-Kermit Tutorial ] [ OpenSSH Home ]


USING THE CONNECTION

[ Top ] [ Contents ] [ Next ]

Once you have an SSH connection to a Kermit server, it's just like any other connection to a Kermit server (and very similar to a connection to an FTP server). You give the client file transfer and management commands for the server, and the server executes them. Of course you can also give the client any other commands you wish. For a quick introduction to Kermit client commands, see:

http://www.columbia.edu/kermit/ckututor.html
C-Kermit tutorial, in particular THIS SECTION.

http://www.columbia.edu/kermit/k95tutor.html
Kermit 95 tutorial

http://www.columbia.edu/kermit/ckututor.html#server
Overview of client/server operation


AUTOMATION

[ Top ] [ Contents ] [ Next ]

Let's look at the basics of SSH script construction. We'll start with Unix simply because it's more script-friendly, and also because scripts are needed in Unix anyway to adapt to different SSH clients (OpenSSH, Data Fellows, Solaris, ...)

Let's dive right in with a simple script that uses the SSH 3.0.2p1 client as its connection agent to make an interactive client/server connection. The following script works with any version of C-Kermit 7.0 or later:

#!/usr/local/bin/kermit +
#
# skermit -- SSH connection to Kermit server subsystem
# Assumes OpenSSH 3.0.2p1 client.
#
# Command-line arguments:
#  1 = host
#  2 = user (optional, defaults to local username)
#
if not def \%1 exit 1 Usage: \%0 host [ user ]
if not def \%2 .\%2 := \v(user)
set exit warning off
set host /pipe ssh -e none -l \%2 -T -s \%1 kermit
if fail exit 1
set reliable on
if echo You might have to wait for a password prompt here...
set input echo on
input 60 KERMIT READY TO SERVE...
.\%9 := \v(status)
echo
if \%9 exit 1 Kermit Server not found
set input echo off
echo
echo You have a Kermit server on the other end of the connection.
echo Use the following commands: SEND, GET, RCD, RPWD, RDIR, ...
echo Close the connection with BYE or FINISH.
echo

Save the script somewhere in your PATH, call it "skermit", and give it execute permission:

chmod +x skermit

Now you can invoke the script in any of three ways:

skermit
This just prints a one-line usage message.

skermit xyzcorp.com
This makes an ssh connection to the given host, using your local username.

skermit xyzcorp.com olga
This makes an ssh connection to the given host, using username "olga".
Once you have the connection, you can give as many commands as you wish to download or upload files, manage remote or local files, or anything else C-Kermit lets you do. When you are finished with the server, give it a BYE or FINISH command, or simply EXIT from C-Kermit.

Now let's look at the script line by line:

#!/usr/local/bin/kermit +
This is the "kerbang" line. It tells Unix that the script is to be interpreted by C-Kermit, whose path in this example is /usr/local/bin/kermit. The plus sign (+) means that command-line arguments should be given to the script as parameters. NOTE: This line must be on the left margin or else Unix won't recognize. Also not that as far as Kermit is concerned, this line (like all others that start with # or ;) is a comment, and has no effect at in Kermit 95.

if not def \%1 exit 1 Usage: \%0 host [ user ]
If there are no command-line arguments, exit with a failure status (1) and a usage message.

if not def \%2 .\%2 := \v(user)
If a username is not supplied on the command line, use the local username.

set exit warning off
Fatal errors in the following commands are handled by an EXIT command. SET EXIT WARNING OFF tells Kermit not ask you if it's "OK to exit?" under these conditions -- just exit.

set host /pipe ssh -e none -l \%2 -T -s \%1 kermit
Tells the external ssh agent to make a connection to the given host, and creates a pipe to the ssh client.

if fail exit 1
If the SET HOST command failed, quit now.

set reliable on
We come here only if the SET HOST command succeeded. This command tells Kermit that we have a reliable end-to-end connection, and is needed because pipes are not normally considered reliable. When Kermit knows the connection is reliable, it attempts to negotiate streaming, a much faster version of the Kermit protocol.

echo You might have to wait for a password prompt here...
Depending on your SSH configuration, the ssh client might prompt you for a password. If it does, the response can not be scripted; you have to type the password at your terminal. Also, if you have never made an SSH connection to this host before, you might be told (by the ssh client) that the host's identity can not be verified and asked whether to continue. This dialog can't be scripted either. On the other hand, if you can use the ssh client to log in to the host without any prompts or passwords, the same thing will happen here. Kermit has no way of knowing. The same thing happens with SFTP.

set input echo on
input 60 KERMIT READY TO SERVE...
When you are successfully authenticated, the remote SSH server starts the Kermit server subsystem. The "KERMIT READY TO SERVE..." message is issued by the remote Kermit server when it is ready to receive commands. You can't send commands to the Kermit server before it is started and ready, but you also don't want to wait unnecessarily. The INPUT 60 command tells the script to wait up to 60 seconds for this message; the INPUT command completes successfully the instant the message appears (as long as this happens within 60 seconds), thus preventing both premature attempts to access the server and overlong waits. SET INPUT ECHO ON lets you see the READY message; if you omit SET INPUT ECHO ON, the script still works but does not show you the message.

.\%9 := \v(status)
This saves the result of the INPUT command (0 = success, 1 = failure) in a local variable around the next command, which must be issued regardless of the INPUT command's completion status.

echo
The ECHO command starts a new line on your screen; otherwise subsequent messages would be appended to "KERMIT READY TO SERVE...".

if \%9 exit 1 Kermit Server not found
If the INPUT command timed out, this prints an error message and quits because no Kermit server has announced itself on the far end.

set input echo off
This just puts INPUT ECHO back to normal.

The remaining ECHO statements just print some simple instructions.

Now let's pay closer attention to the SET HOST command that starts the ssh client:

set host /pipe ssh -e none -l \%2 -T -s \%1 kermit

As explained in the C-Kermit 7.0 documentation, SET HOST /PIPE makes a "network" connection, but instead of to a true network like the Internet, it makes it to an external program through a pair of pipes -- one for input, one for output. Unlike the PIPE command itself, SET HOST /PIPE does not include an implied CONNECT command. We use SET HOST /PIPE instead of PIPE because we are not making a terminal connection (if we did, it would only connect us to the server's packet reader).

C-Kermit also has another way of controlling subprograms such as ssh: the SET HOST /PTY command, but we don't use it in this case simply because it doesn't work.

After SET HOST /PIPE comes the ssh invocation itself:

ssh -e none -l \%2 -T -s \%1 kermit

which happens to apply to the OpenSSH 3.0.2p1 client; the options are explained in "man ssh".

[ C-Kermit Scripts ] [ C-Kermit 7.0 Documentation ] [ C-Kermit 8.0 Documentation ]


MAKING THE SCRIPT PORTABLE TO KERMIT 95

[ Top ] [ Contents ] [ Next ]

In Windows, if you save a Kermit script file with a suffix of .KSC, this associates it with Kermit 95. Clicking on any file named this way causes Kermit 95 to start and execute commands from the file. But unfortunately, there is no way to pass parameters to a script when you invoke it in this way. But you can pass parameters if you invoke it from a command window, or from within Kermit 95 itself. Thus the first adaptation is to make the script prompt for (or supply) any missing parameters:

while not def \%1 {
    ask \%1 { Host: }
}
if < \v(argc) 2 {
    ask \%2 { User [\v(user)] }
}
if not def \%2 .\%2 := \v(user)

If the first command-line parameter, \%1 (the hostname) is missing, we prompt for it until it is given. Then, only if a hostname was not given on the command line, we prompt for a username. Then if there still is no username, we supply the local username. This allows for all possible invocations:

skermit
Prompts for hostname and username

skermit hostname
Uses local username

skermit hostname username
Uses the given username

The second adaptation allows for the different commands that must be used to make SSH connections in C-Kermit and Kermit 95. The key commands for adaptations like this are:

IF K-95 command
Executes the command if the program is Kermit 95.

IF C-KERMIT command
Executes the command if the program is C-Kermit.

Here we execute the appropriate connection command:

if k-95 {
    set host /network:ssh /user:\%2 \%1 /subsystem:kermit
} else {
    set host /pipe ssh -e none -l \%2 -T -s \%1 kermit
}

And here we print the "You might have to wait" message only if it's not Kermit 95:

if not k-95 echo You might have to wait for a password prompt here...

This could also have been written as:

if c-kermit echo You might have to wait for a password prompt here...

Now the same script can be used in Unix and Windows. Here is the portable version of the script in full:

#!/usr/local/bin/kermit +
#
# skermit -- SSH connection to Kermit server subsystem
# Assumes OpenSSH 3.0.2p1 client.
#
# Command-line arguments:
#  1 = host
#  2 = user (optional, defaults to local username)
#
while not def \%1 {
    ask \%1 { Host: }
}
if < \v(argc) 2 {
    ask \%2 { User [\v(user)] }
}
if not def \%2 .\%2 := \v(user)
set exit warning off
if k-95 {
    set host /network:ssh /user:\%2 \%1 /subsystem:kermit
} else {
    set host /pipe ssh -e none -l \%2 -T -s \%1 kermit
}
if fail exit 1
set reliable on
if not k-95 echo You might have to wait for a password prompt here...
set input echo on
input 60 KERMIT READY TO SERVE...
.\%9 := \v(status)
echo
if \%9 exit 1 Kermit Server not found
set input echo off
echo
echo You have a Kermit server on the other end of the connection.
echo Use the following commands: SEND, GET, RCD, RPWD, RDIR, ...
echo Close the connection with BYE or FINISH.
echo


EXPANDING THE SCRIPT

[ Top ] [ Contents ]

The simple "skermit" script shown above gives you the connection but leaves the rest to you. Obviously the script could be extended to do whatever else you want:

Just replace the block of ECHO commands at the end with whatever other commands you want. Here's an example in which fetch one file and quit. First we add a WHILE command to check for a third command-line parameter:

while not def \%3 {
    ask \%3 { File to Get: }
}

Then at the end, we put the commands to get the file, disconnect, and exit:

get \%3
bye
exit

In which:

get \%3
This command tells the client to ask the server to send the desired file.

bye
Shuts down and disconnects from the server. If you omit this command, nothing terrible happens, but it's more polite (and who knows, maybe also safer) to shut down the server gracefully than to have Unix kill it because the connection was lost.

exit
If the connection is still open (it won't be if a BYE command was given), this command closes it. In any case, it exits from Kermit back to the environment from which the script was invoked: shell prompt, some other script, desktop, whatever. The default exit status code is nonzero if the GET command failed. If the GET succeeded, the exit status code is zero.

Here's a slightly more ambitious example. Suppose every time you run this script, you want to CD to certain remote and local directories and refresh the local directory with any new files from the remote one:

define serverdir /usr/olga/orders/
define localdir c:/olaf/orders/
rcd \m(serverdir)
if fail exit 1 Server directory \m(serverdir) not found
cd \m(localdir)
if fail exit 1 Failure to CD to client directory \m(localdir)
set file collision update
get *
bye
exit

Let's step through this line by line:

define serverdir /usr/olga/orders/
define localdir c:/olaf/orders/
Assigns the remote and local directory names to variables so if we ever need to change them, they need to be changed only in one place. Of course they could also be parameters, or the script could pick them up from environment variables, or any other conceivable arrangement.

rcd \m(serverdir)
Tells the server to change directory to the desired remote directory.

if fail exit 1 Server directory \m(serverdir) not found
Checks to make sure the RCD command succeeded. If it didn't, and we didn't check, we would be downloading files from the wrong directory.

cd \m(localdir)
Tells the client to change directory to the desired local directory.

if fail exit 1 Failure to CD to client directory \m(localdir)
Check this too, so we don't download files into the wrong directory.

set file collision update
Tells the client to reject any incoming files that are not newer than their local counterparts (if any).

get *
Asks the server to send all files from its current directory. It doesn't matter whether they are text, binary, or any mixture, even if this is a cross-platform transfer (e.g. Unix to Windows): the Kermit protocol handles it all automatically. Because the client has set FILE COLLISION UPDATE, only updated or new files are sent; the rest are skipped automatically.

bye, exit
As above.

Build on these examples. The sky is the limit. Begin by visiting the Kermit scripting tutorial at the website.

[ Top ] [ Contents ] [ K95 Home ] [ C-Kermit Home ] [ C-Kermit Scripting ] [ Kermit Home ]


Kermit SSH Subsystem / The Kermit Project / Columbia University / kermit@columbia.edu / 16 April 2002