Name
Dropbear — Ssh client support
Synopsis
#include <dropbear.h>
int cyg_dropbear_ssh_connect(
cyg_dropbear_cli_handle* handle, const struct sockaddr_storage* addr, const cyg_dropbear_authenticate* auth, const char *
command)
;
void cyg_dropbear_ssh_close(
cyg_dropbear_cli_handle* handle, int wait)
;
Description
The client-side API allows an eCos application to establish a secure connection to a remote ssh server and run commands on the remote machine. This requires that the application authenticate itself as a valid user on that system. Once the remote command is running the eCos application can interact with its stdin/stdout/stderr stream over sockets.
The client-side code has only been tested against openssh running on a Linux server and and as such descriptions of host-side server configuration settings and files in the remainder of this section refer to Linux. Interoperability with other ssh implementations cannot be guaranteed.
Application developers should be aware that establishing an ssh
connection is a complicated business. Even if the eCos application is
working correctly there are many things completely outside its control
that could go wrong and prevent a secure connection from being
established. Some of these are: firewalls intercepting and discarding
packets to the ssh server; tcp wrappers intercepting and rejecting
requests before they even reach the ssh server, courtesy of settings
in the /etc/hosts.allow
and
/etc/hosts.deny
files on the host ssh server;
ssh server settings in /etc/ssh/sshd_config
which are incompatible with the application's requirements; problems
with the user account specified by the application; or problems with
the ssh keys in the ~/.ssh/authorized_keys2
file
of the user's account on the host server. It is recommended that when
experiencing connectivity problems from an eCos application the
developer first checks the server's setup, for example by using
ssh or dbclient
commands on a suitable Linux box on the same network as the eCos
system and specifying the same account and keys.
Application developers should also be aware that allowing remote systems running eCos to access an ssh server has security implications. For example if an attacker has physical access to a remote system, that attacker could use technology like jtag to examine the contents of the flash memory and search for plain text passwords or private keys. It is the developers' responsibility to understand the security issues associated with ssh technology and decide whether the risks are acceptable.
API
There are only two functions in the client-side API, one to establish a secure connection and run a command on the remote machine, the other to shut down the connection cleanly. The key data structure is a cyg_dropbear_cli_handle which holds all application-level state relevant to the connection.
typedef struct cyg_dropbear_cli_handle { int db_stdin_stdout; int db_stderr; int db_exitcode; char db_error[CYG_DROPBEAR_MAX_ERROR]; … } cyg_dropbear_cli_handle;
Each ssh connection requires its own instance of this data structure,
and the instance must exist for the duration of the connection. All of
the fields are managed by the dropbear code and the application should
only read them, not modify them in any way. The
db_stdin_stdout
and
db_stderr
fields are file descriptors
corresponding to sockets. Any data written to
db_stdin_stdout
will appear
on the remote application's stdin stream. Any data written by
the remote application to its stdout will appear as input on
db_stdin_stdout
. Any data written to its
stderr will appear as input on db_stderr
.
The db_exitcode
field is only valid once
the remote application has exited and hold its exit code, usually 0
for a successful run and non-zero to indicate some kind of error.
Most error conditions associated with the ssh connection itself will
result in an error message being placed in
db_error
. However error conditions within
the remote command will typically be reported via the stderr stream.
A typical client-side application will look like this:
void run_remote_program(…) { cyg_dropbear_cli_handle handle; <Fill in a struct sockaddr with the server's network address> <Fill in an authentication structure> if (!cyg_dropbear_ssh_connect(&handle, …)) { <Something has gone wrong during the connect process> <If there is a user, report the handle's db_error message> return; } if ( <reading from remote application> ) { while (! <EOF detected on db_stdin_stdout> ) { <Use read() on db_stdin_stdout> <Optionally, for robustness, look for errors on db_stderr> } } else { // writing to remote application while ( <there is data to be written> ) { <use write() on db_stdin_stdout> <Optionally, for robustness, look for errors on db_stderr> } } cyg_dropbear_ssh_close(&handle, 1); <Optionally check the exit code> }
More complicated behaviour is possible, and the
clitest1.c
testcase in the package's
tests
subdirectory
provides numerous examples.
Connecting
The cyg_dropbear_ssh_connect
function takes four
arguments:
-
handle
-
A pointer to a cyg_dropbear_cli_handle
structure. This structure will be filled in and managed by the
dropbear code, and should only be read by the eCos application.
The structure must remain valid for the duration of the ssh
connection, until after the call to
cyg_dropbear_ssh_close
. -
addr
-
The full address of the ssh server on the remote machine. Typically
this will actually be a sockaddr_in or
sockaddr_in6 structure (assuming
CYGPKG_NET_INET6
is enabled). The ssh server must be accessible via this address irrespective of any firewall filtering, tcpwrapper settings (/etc/hosts.allow
and/etc/hosts.deny
), and ssh server settings (/etc/ssh/sshd_config
on the host, especially theAddressFamily
,ListenAddress
andPort
settings). Usually the port number will behtons(22)
but an alternative ssh server listening on a different port may also be used. The dropbear code does not examine the contents of the address, it simply passes the address on to the TCP/IP stack'sconnect
function. -
auth
- This structure holds all the authentication information, and is discussed in detail below.
-
command
-
The command to be executed on the remote server. Typically this will
be run using the account's default shell with a
-c <command>
option. IfNULL
is passed then the remote ssh server will start an interactive shell.
If the connection attempt succeeds and the remote ssh server starts
the remote shell then cyg_dropbear_ssh_connect
will return 1 and the db_stdin_stdout
and
db_stderr
fields in the handle structure
will be filled in with suitable sockets. Note that this does not mean
that the remote shell has successfully started the requested command.
If that part of the operation fails then the shell will output an
error message on stderr and exit.
If the connection attempt fails because of a lack of resources,
because the remote ssh server is not accessible, or because of an
authentication failure, then
cyg_dropbear_ssh_connect
will return 0 and the
handle's db_error
field will contain an
error message.
Internally, establishing an ssh connection involves starting a
separate worker thread and it is the worker thread which runs the
main dropbear code. It accepts messages over the network socket from
the remote ssh server, decrypts them, and forwards them over local
sockets to the application's
db_stdin_stdout
and
db_stderr
. It also accepts data written to
db_stdin_stdout
via a local socket,
encrypts it, and passes it on to the remote application via the
network socket and the ssh server.
Authentication
The cyg_dropbear_authenticate structure
passed to cyg_dropbear_ssh_connect
holds the
information needed to authenticate the connection with the remote ssh
server. Most of the fields are optional, as long as at least one valid
authentication mechanism is provided. The structure contains the
following fields:
typedef struct cyg_dropbear_authenticate { const char* db_username; const char* db_host_rsa_key_pub; const char* db_host_dsa_key_pub; const char* db_id_rsa; int db_id_rsa_keylen; const char* db_id_dsa; int db_id_dsa_keylen; const char* db_password; } cyg_dropbear_authenticate;
The structure can be constructed at run-time or statically allocated.
The dropbear code only reads the various fields during the call to
cyg_dropbear_ssh_connect
.
The db_username
field must be filled in. It
should be a simple string corresponding to a valid account name on the
ssh server machine, for example:
struct cyg_dropbear_authenticate auth; … auth.db_username = "dropbeartest";
The account name must also be one allowed by the ssh server, as per
the /etc/ssh/sshd_config
file's
AllowUsers
setting on the host.
The rsa and dsa host keys can be used to validate the identity of the
remote ssh server, preventing certain man-in-the-middle attacks. These
fields serve much the same purpose as the
~/.ssh/known_hosts
file when using the
ssh command on a Linux system. During the
authentication stage of establishing a connection the remote ssh
server will send a signature encrypted using the server's private key,
and the public keys can be used to validate this signature. The fields
should be initialized using the contents of the
/etc/ssh/ssh_host_dsa_key.pub
and
/etc/ssh/ssh_host_rsa_key.pub
files on the host
server, for example:
… auth.db_host_rsa_key_pub = "ssh-rsa AAAAB3Nz…rb8="; auth.db_host_dsa_key_pub = "ssh-dss AAAABDNz…v7s=";
Note that the host's /etc/ssh/sshd_config
file can
specify alternative keys using HostKey
settings.
It is not necessary to supply the public host keys to the dropbear
code. If neither host key is supplied then the code will simply not
attempt to validate the identity of the remote ssh server and the
known_hosts
protection is not applied.
Specifying the public host keys in the authentication structure has
one major disadvantage. If it ever becomes necessary to change the host
keys on the ssh server then the eCos boxes will not be able to connect
to the remote ssh server until the boxes are updated with a new host
key. This behaviour is different from running ssh
interactively on the Linux command line where the user will be given
the choice of accepting the new key and updating the
known_hosts
file.
The db_id_rsa
,
db_id_rsa_keylen
,
db_id_dsa
and
db_id_dsakeylen
fields are used to hold the
private keys for public key authentication. If neither key is supplied
then the dropbear code will only attempt password authentication. It
should be noted that establishing a secure connection using an RSA
private key requires many more cpu cycles than using a DSA private
key. If both keys are supplied then the dropbear code will try the DSA
key first, then the RSA key. The overheads can be reduced by using a
smaller keysize, but obviously that has security implications.
The dropbear code requires its private keys in a different format from
the Linux ssh command, and embedding these keys
requires a somewhat convoluted process. One approach involves
generating the keys using the host's ssh-keygen
utility with -t rsa
or
-t dsa
. No passphrase must be used, and
great care should be taken not to overwrite the user's default
private key file. The ssh private key can then be converted into a
dropbear key using the dropbearconvert utility,
either built from the sources or installed via e.g. the standard
dropbear package.
Notes | |
---|---|
|
The dropbear-format db_id_dsa
file can then be
converted into a C array using the privatekey2c
utility supplied in the package's
misc/hangman
and
misc/shell
directories.
For example, to generate a set of private keys for both dsa and rsa, convert them into dropbear format, and generate a C array for inclusion into an eCos client:
ssh-keygen -m PEM -t rsa -C "dropbear@ecoscentric.com" # Create rsa key ssh-keygen -m PEM -t dsa -C "dropbear@ecoscentric.com" # Create dsa key dropbearconvert openssh dropbear ~/.ssh/id_rsa db_id_rsa # Convert to dropbear format dropbearconvert openssh dropbear ~/.ssh/id_dsa db_id_dsa # Convert to dropbear format privatekey2c id_rsa db_id_rsa > id_rsa.c # Create C code for eCos privatekey2c id_dsa db_id_dsa > id_dsa.c # Create C code for eCos
The resulting id_dsa.c
file will contain code
like the following:
#define ID_DSA_LEN 457 static const char id_dsa[ID_DSA_LEN] = { 0x00, 0x00, 0x00, 0x07, \ … \ 0xd3 \ };
The resulting file can be #include'd by the application code and used
to fill in the auth structure's db_id_dsa
and db_id_dsa_keylen
fields.
As an alternative to using the ssh-keygen and dropbearconvert utilities, dropbear's dropbearkey can be used to generate the private key files directly in the dropbear format. It will still be necessary to use privatekey2c to turn the keys into a C array.
The remote ssh server will use the account's
~/.ssh/authorized_keys
file to validate the
private keys supplied by the eCos application. This file must be
created or updated with the corresponding public keys. For example:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys chmod go-rw ~/.ssh/authorized_keys
Note | |
---|---|
Prior to version 3 of OpenSSH, public key authentication used the
file |
It is also necessary to have PubkeyAuthentication
enabled in /etc/ssh/sshd_config
on the ssh
server host, and if DSA is to be permitted,
PubkeyAcceptedKeyTypes
must include
ssh-dss
.
The final field in the authentication structure is
db_password
. Again this should be a C
string holding the plaintext password, or NULL if password
authentication should not be attempted. The ssh server will only allow
plaintext passwords if PasswordAuthentication
is
enabled in the server's configuration file.
I/O with the remote process
Once a secure connection has been established using
cyg_dropbear_ssh_connect
the eCos application
code can interact with the remote command using the
db_stdin_stdout
and
db_stderr
fields in the connection's handle
structure. These fields hold file descriptors corresponding to local
sockets, and the other ends of these local sockets are managed by an
internal worker thread started by the connect operation and running
the bulk of the dropbear code. Typically I/O involves the
read
, write
and
select
calls. Any data written to
db_stdin_stdout
will be read by the worker
thread, encrypted, forwarded to the remote ssh server over the network
connection, decrypted, and can be read by the remote command using its
stdin stream. Any data written by the remote command to stdout or
stderr will follow the reverse path and can be read by the eCos
application code using the db_stin_stdout
and db_stderr
file descriptors.
If the remote command exits while the eCos application is still
reading data, this will result in an end-of-file condition on the
db_stdin_stdout
file descriptor. In other
words a read
call will return 0. At that point
the application should close the connection using
cyg_dropbear_ssh_close
, and not by using the
close
call on the file descriptor. Similarly if
the eCos application wants to close the connection while the remote
command is still running then it should use
cyg_dropbear_ssh_close
.
Given the complexity of the data flow, details of any error conditions
such as broken network connections will not get as far as the
application code. Specifically, the value of the
errno
variable will not correspond to the
underlying error condition. Instead error conditions are likely to
manifest as end-of-file conditions indistinguishable from the command
exiting.
Closing a connection
When the eCos application wants to close down the ssh connection it
should call cyg_dropbear_ssh_close
. This takes
two arguments, the handle filled in by
cyg_dropbear_ssh_connect
and a flag to indicate
whether or not the application wants to wait for the remote command to
exit. If the application does not wait then the
db_exitcode
field may not be valid, and the
internal worker thread will continue to run in the background and
consume resources until the remote command has finished.
Configuration
There are no configuration options specific to the client-side API, other than ones related to testing as described below. However there are a number of options common to the server-side and client-side API.
-
CYGNUM_NET_DROPBEAR_MAX_PAYLOAD_LEN
- While establishing a secure connection the client and daemon negotiate an upper limit for the outgoing packet size. This has to be large enough to cope with the initial key exchange and related information. For eCos the default size is set to 1K, which should be sufficient but is still rather smaller than the dropbear default on other platforms. If the application involves transferring large amounts of data over ssh then throughput may be improved by increasing this packet size, at the cost of increased memory usage.
-
CYGNUM_NET_DROPBEAR_THREAD_PRI
- The client-side dropbear code involves an internal worker thread for each secure connection. These worker threads run at the same priority controlled by this configuration option. The ssh protocol involves computationally intensive operations such as encrypting and decrypting packets, so these threads may have a significiant impact on the number of cpu cycles available to the rest of the system. It may be necessary to manipulate the thread priority to achieve an acceptable balance between overall system performance and the achievable ssh bandwidth.
-
CYGNUM_NET_DROPBEAR_THREAD_STACKSIZE
- This configuration option controls the size of the stacks allocated for internal worker threads. Fairly large stacks are needed to implement the ssh protocol, and in addition the various application callback functions will also run on these stacks. The default value should suffice for all architectures, but this can be checked by using a debug build with stack checking enabled. If the stack sizes are reduced to save memory then it is strongly recommended that the system be tested with stack checking enabled.
Testing
The client-side code comes with an extensive testcase
tests/clitest1.c
. However this testcase is not
built by default. It requires information about the testing
environment such as the address of a suitable ssh server, as well as
authentication information such as private keys and plain-text
passwords. Embedding that kind of information directly into
readily-available source code would have obvious security implications.
Instead it is necessary to set a variety of configuration options
before the testcase can be built.
Not all relevant configurations options need to be set. For example if
the booldata option
CYGTST_NET_DROPBEAR_TESTS_CLI_PASSWORD
is not
enabled then the testcase will not attempt any tests related to
plain-text passwords. Details of the authentication mechanisms and
relevant server settings can be found in the authentication section
above.
Some of the configuration values are long, for example complete
private ssh keys. Entering these using the graphical configuration
tool may be problematical. Instead the ecos.ecc
savefile can be edited directly. Alternatively the dropbear package
comes with a file misc/clitest1.ecm
, containing
the configuration options related to testing. This file can be edited
and then imported into the eCos configuration. Where appropriate the
testcase will turn the configuration values into C strings
automatically, so there is no need to add extra quotes to the values.
The relevant options are:
-
CYGPKG_NET_DROPBEAR_TESTS_CLI
- Unless this option is enabled the client-side testcase will not be built.
-
CYGTST_NET_DROPBEAR_TESTS_CLI_SERVER_ADDR
This should be the address of the remote ssh server in a format acceptable to the standard
inet_pton
function, for example 192.168.0.42.cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_SERVER_ADDR { user_value 192.168.0.42 };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_SERVER_PORT
This should be the tcp port number for the remote ssh server. It should be 22 when interacting with the system's standard ssh server.
cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_SERVER_PORT { # user_value 22 };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_AF
This should be the address family,
AF_INET
for IPv4 testing orAF_INET6
for IPv6 testing.cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_AF { # user_value AF_INET };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_USERNAME
This should be the name of the testing account on the ssh server machine.
cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_USERNAME { user_value dropbeartest };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_HOST_RSA_KEY_PUB
The public RSA host key for the remote ssh daemon. On a modern debian Linux systems, this can be found in
/etc/ssh/ssh_host_rsa_key.pub
. Note that the trailing comment, normally the hostname, should be ommitted.cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_HOST_RSA_KEY_PUB { user_value 1 "ssh-rsa AAAA…rb8=" };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_HOST_DSA_KEY_PUB
The public DSA host key for the remote ssh daemon. On a modern debian Linux systems, this can be found in
/etc/ssh/ssh_host_dsa_key.pub
. Note that the trailing comment, normally the hostname, should be ommitted.cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_HOST_DSA_KEY_PUB { user_value 1 "ssh-dss AAAA…v7s=" };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_ID_RSA
This value should be the contents of a private RSA key. This can be obtained using the privatekey2c utility as described above in the authentication section. The contents of the C array in the resulting source file can then be copied and pasted into the .ecm or .ecc file.
cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_ID_RSA { user_value 1 " \ 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, \ … \ 0x76, 0xfe, 0x15" };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_ID_DSA
This value should be the contents of a private DSA key. This can be obtained using the privatekey2c utility as described above in the authentication section. The contents of the C array in the resulting source file can then be copied and pasted into the .ecm or .ecc file.
cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_ID_DSA { user_value 1 " \ 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, \ … \ 0x1d" };
-
CYGTST_NET_DROPBEAR_TESTS_CLI_PASSWORD
This value should be the plain-text password used for authenticating the user.
cdl_option CYGTST_NET_DROPBEAR_TESTS_CLI_PASSWORD { user_value 1 opensesame };
SSH Server Host Configuration
Modern Linux hosts are normally tightly configured with respect to
ssh access so as to minimise risk of being compromised. When using
an OpenSSH server on Linux as the test host, you may wish to ensure
access to the host is not compromised and is restricted. For
example, you may only wish to permit
access by the dropbear clitest1 and
scptest1 tests to the user specified in
CYGTST_NET_DROPBEAR_TESTS_CLI_USERNAME
from
internal network addresses used by the test hardware. For example,
if the test hardware were located on the internal IPv4 segment
192.168.8.0/24 and internal IPv6 segment fd54:5cd1:6fc7:1619::/64,
the configuration file /etc/ssh/sshd_config
could contain the following configuration extract that permits
password and DSA autentication for the user
dropbeartest
from either network:
# Permit dropbear test user to connect from internal test networks and use password authentication Match User dropbeartest Address 192.168.8.0/24,fd54:5cd1:6fc7:1619::/64 PasswordAuthentication yes PubkeyAcceptedKeyTypes=+ssh-dss
When testing the user account from another Linux server using
ssh, the user file
~/.ssh/config
also should contain the
following line to permit DSA private key authentication as that
client host may likely have such authentication prohibited:
PubkeyAcceptedKeyTypes +ssh-dss
2024-03-18 | eCosPro License |