Name
Dropbear — Ssh daemon support
Synopsis
#include <dropbear.h> extern int cyg_dropbear_connections;
void cyg_dropbear_init(
const cyg_dropbear_data* data)
;
void cyg_dropbear_done(
cyg_dropbear_handle handle, int exit_code)
;
int cyg_dropbear_get_stderr(
cyg_dropbear_handle handle)
;
const char* cyg_dropbear_get_username(
cyg_dropbear_handle handle)
;
const struct sockaddr_storage* cyg_dropbear_get_addr(
cyg_dropbear_handle handle)
;
Description
This document assumes the reader has a basic understanding of both ssh
operation and of TCP/IP network programming. Information on both of
these can be readily obtained from other sources. The eCos
port of dropbear does behave rather differently from dropbear or other
ssh implementations in a conventional setup such as a network of Linux
workstations. eCos does not support multiple processes or shell
executables. The dropbear code runs in separate threads within the
eCos application, started by a call to
cyg_dropbear_init
, rather than as a separate
daemon process. Authentication does not involve reading in
files such as ~/.ssh/authorized_keys2
or
/etc/passwd
. Instead the application developer
has to write a number of callback functions to supply the necessary
information and can decide how best to implement those functions -
which may involve file I/O if that happens to be convenient. When an
ssh connection is established the dropbear code will not start up a
new shell process inside a pseudo terminal. Instead the dropbear code
will create a local TCP/IP socket and pass one end of this to the
application. Data coming from the network will be decrypted and sent
down the local socket, to be read by application code. Data written by
the application to the local socket will be encrypted and sent over
the network to the ssh client.
The eCos dropbear package comes with two working examples in the
misc/hangman
and
misc/shell
directories which should be
examined in conjunction with this documentation.
API
The internals of the dropbear code are not directly accessible to application code. Instead dropbear runs in a number of internal threads, interacting with the application via local TCP/IP sockets and a number of callback functions.
Initialization
The main entry point to dropbear functionality is the function
cyg_dropbear_init
. This should be called by the
application when the dropbear code can start running, any time after
the network connection has been activated. It takes a single argument, a
pointer to a cyg_dropbear_data data
structure. That structure should be filled in by the application with
information needed by the dropbear code, including pointers to various
callback functions.
typedef struct cyg_dropbear_data { const char* db_rsa_private_host_key; int db_rsa_private_host_keylen; const char* db_dss_private_host_key; int db_dss_private_host_keylen; cyg_bool (*db_accept_connection)(struct sockaddr_storage*); void (*db_connected)(int, cyg_dropbear_handle); void (*db_get_public_keys)(char*, char*, struct sockaddr_storage*, char **); void (*db_free_public_keys)(char**); cyg_bool (*db_authenticate_password)(char*, struct sockaddr_storage *, char*); void (*db_logger)(const char*, va_list); } cyg_dropbear_data;
The cyg_dropbear_data
structure will
be accessed regularly by the dropbear code so must not be overwritten
or freed by the application. Typically it will be statically allocated.
The initialization function will start the main dropbear thread,
running at configurable priority
CYGNUM_NET_DROPBEAR_THREAD_PRI
. This thread will
initialize the dropbear package. It will then bind to the desired
TCP/IP port on address INADDR_ANY
. The default port
is 22 as per ssh but this can be changed via
CYGNUM_NET_DROPBEAR_NETWORK_PORT
. The thread will
accept new connections from remote clients and start a separate worker
thread for each connection, again running at priority
CYGNUM_NET_DROPBEAR_THREAD_PRI
. Usually the main
dropbear thread will also assist with setting up the local connection.
cyg_dropbear_init
will return once the main
thread has been started.
Host Keys
As an ssh daemon the dropbear code requires a private host key. This
can be either a DSS key or an RSA key, but preferably both should be
supplied. Private keys can be generated using the
dropbearkey host utility. The host key is used
primarily by clients to uniquely identify a given machine, in
conjunction with the ~/.ssh/known_hosts
file.
The application should supply one or two host keys via the
db_rsa_private_host_key
,
db_rsa_private_host_keylen
,
db_dss_private_host_key
and
db_dss_private_host_keylen
fields of the
cyg_dropbear_data structure passed to
cyg_dropbear_init
.
Conventional ssh usage dictates that all units should have unique
private host keys. That implies that the host keys are held in a
file system or in non-volatile flash memory and customized on a
per-unit basis. Generating new host keys on startup is undesirable
because it would result in different keys after every reboot,
defeating the purpose of the known_hosts
file. In
practice it may be acceptable to use a single set of host keys for all
units, but it is up to the application developer to consider the
security implications.
New Connections Callback
When the main dropbear thread accepts a new ssh connection from the network it will start a new worker thread to handle that connection. Before any data is exchanged over the TCP/IP socket the worker thread will call back into the application so that the latter can decide whether or not a new connection should be accepted at this time.
cyg_bool application_accept_connection_callback(struct sockaddr_storage* addr) { … }
A pointer to this callback function should be stored in the
db_accept_connection
field of the
cyg_dropbear_data structure passed to
cyg_dropbear_init
. The callback's argument is the
address of the connecting client. Typically this will be an IPv4
sockaddr_in address but the
ss_family
field of the
sockaddr_storage structure should be checked.
The client's address can be used to restrict incoming connections to
certain networks or even to individual hosts. This offers some
increased security along similar lines to a firewall, albeit only for
ssh traffic, but it may cause problems in future if it ever becomes
necessary to access the unit from a different network or host.
Another common use for the new connections callback is to limit the
number of concurrent connections, and thus limit the resources
consumed. To facilitate this the dropbear code exports an integer
cyg_dropbear_connections
which holds the current
number of client connections.
The callback should return zero if the new connection should be declined, non-zero if the dropbear code should proceed with key exchange and authentication.
The use of a new connection callback is optional. If the application
does not want to perform any checks at this time then it can use a
NULL pointer for the db_accept_connection
field.
Key Authentication Callback
After the new connections callback the dropbear worker thread will
engage in a key exchange with the client. This allows the client to
verify information in the known_hosts
file with
the daemon's private host key or keys. The exchange also involves
negotiating a mutually acceptable encryption protocol and deciding
what user authentication mechanisms are supported. The eCos dropbear
daemon supports public/private key authentication and password
authentication.
For public/private key authentication the client will generate a
message using the private key, and the daemon can validate this
message using the public key. In a conventional ssh setup the public
keys come from a file ~/.ssh/authorized_keys
or
~/.ssh/authorized_keys2
, but the eCos dropbear
port does not assume the presence of a file system. Instead it is the
application's responsibility to provide the public keys, and a
callback mechanism is used.
void application_get_public_keys(char* algo, char* username, struct sockaddr_storage* addr, char** keys) { … } void application_free_public_keys(char** keys) { … }
Pointers to these callback functions should be stored in the
db_get_public_keys
and
db_free_public_keys
fields of the
cyg_dropbear_data structure passed to
cyg_dropbear_init
. The first argument to the
get_public_keys callback identifies the type of key, usually
ssh-dss
or ssh-rsa
. The second
and third arguments supply the user name and the client's network
address. Together these three arguments can be used to restrict which
public keys are supplied to the dropbear code for validation. Strictly
this is optional since the public keys for e.g. a different user are
not going to match anyway, but the validation requires significant cpu
cycles.
The fourth argument points at a static array of up to
CYGNUM_NET_DROPBEAR_MAXIMUM_PUBLIC_KEYS
entries.
This array will be initialized to NULL pointers. The get_public_keys
callback should fill in zero or more array entries with pointers to
public key strings. These strings may be statically or dynamically
allocated. In case of the latter, the free_public_keys callback will be
invoked to release the memory when the validation has finished.
Within the dropbear code a mutex is used to ensure that only one worker thread at a time will attempt public key authentication. Hence the callback functions can assume that there will be only one call to get_public_keys at a time and that free_public_keys will be called before the next get_public_keys. This allows the callbacks to use static data with no need for additional locking.
Support for public/private key authentication is optional. If the
application is only interested in password authentication then it can
use a NULL pointer for the
db_get_public_keys
field. If all public
keys are statically allocated then a NULL pointer can be used for
db_free_public_keys
field.
Password Authentication Callback
If public/private key authentication fails then the dropbear code can support password authentication instead. The ssh client will encrypt the password before sending it over the wires. The dropbear code will decrypt it and will pass the plain text password to an application callback for verification.
cyg_bool application_authenticate_password(char* username, struct sockaddr_storage* addr, char* password) { … }
A pointer to this callback function should be stored in the
db_authenticate_password
field of the
cyg_dropbear_data structure passed to
cyg_dropbear_init
.
It is up to the application developer to decide how to validate the
password. A very simple approach would just involve comparing it with
a constant string. However that approach is not very secure: if there
is any way to read the unit's memory directly, for example via jtag
debug hardware, then it will be possible to extract the password
directly from memory. A more advanced approach would involve
encryption, for example using much the same techniques as for a Unix
/etc/passwd
file.
Support for password authentication is optional and can be disabled simply by not supplying the callback function.
Connected Callback
In a conventional ssh environment, once authentication has succeeded
the daemon will open a pseudo tty, fork a new process, and start a
shell running in that process. eCos does not have pseudo ttys,
processes, or shells, so a different approach is needed. The dropbear
worker thread for this ssh session will establish a local socket
connection and will pass this socket on to the application. Encrypted
data coming from the network will be decrypted by the dropbear worker
thread and then sent down the local socket to the application. When
the application writes plain text data down the local socket the
dropbear worker thread will read it, encrypt it, and sent it to the
ssh client over the network. As far as the application is concerned it
can just read
and
write
data via the local socket, and it need not
concern itself with ssh protocols or encryption. Other standard
network programming techniques such as select
can
be used as desired.
To inform the application that authentication has succeeded and that the local socket connection has been established, the dropbear code will invoke another callback function:
void application_connected(int socket, cyg_dropbear_handle handle) { … }
A pointer to this function should be placed in the
db_connected
field of the
cyg_dropbear_data structure passed to
cyg_dropbear_init
. The first argument is a file
descriptor for the local socket, for use by the application code.
The second argument is a handle that can be used to get additional
information about the connection, and that must be used to shut down
the connection.
The connected callback runs from inside the main dropbear thread. It
should not run the relevant application code directly, since that
would prevent the dropbear code from accepting any new ssh
connections. Instead the callback should arrange for some other part
of the application to handle traffic for this connection. This can be
done in various ways, for example by waking up an existing thread,
creating a new one, or adding the socket file descriptor to an
fd_set structure which gets monitored
via select
elsewhere in the application.
The socket passed to the connected callback is still an ordinary
blocking socket, but can be turned into a non-blocking socket if
desired using the standard FIONBIO
ioctl
. In the absence of pseudo tty support the
socket provides raw terminal functionality. Typically input will
appear a character at a time rather than a line at a time, and any
line editing functionality has to be supplied by the application.
It is also up to the application to implement carriage return/linefeed
processing, to handle special input characters like ctrl-C or ctrl-D,
and so on.
Strictly the use of the connected callback is optional. Application developers can choose to accept the local socket connection in their own code rather than leave it to the main dropbear thread, as that may make it easier to port existing code. It is still necessary to obtain the cyg_dropbear_handle so that the connection can be closed down correctly. The hangman example program illustrates how to do this.
stderr
channel
By default the dropbear code only sets up stdin and stdout channels.
If a stderr channel is needed as well then the configuration option
CYGIMP_NET_DROPBEAR_SUPPORT_STDERR
should be
enabled. An additional function
cyg_dropbear_get_stderr
then becomes available.
This function should be called only from the connected callback. It
returns a file descriptor for a new local socket which can be used for
stderr output, or -1 if the system has run out of sockets or file
descriptors.
Use of cyg_dropbear_get_stderr
is optional, so
applications can decide on a per-connection basis whether or not a
stderr channel is needed.
Connection Information
Once a connection has been established and the connected callback has
been invoked, application code can retrieve some additional
information about this connection.
cyg_dropbear_get_username
will provide the user
name supplied during authentication, and
cyg_dropbear_get_addr
will return the network
address of the ssh client.
Closing Connection
An ssh connection can be shut down in one of two ways. If the client
is terminated then the eCos application will detect an end of file
condition on the local socket and can close its end of the connection.
Alternatively the eCos application can decide to close its end
unilaterally. Either way closing the connection involves two steps.
The close
system call should be used on the file
descriptor for the local socket, and on the one for the stderr socket
if cyg_dropbear_get_stderr
has been used. Next
cyg_dropbear_done
should be invoked using the
cyg_dropbear_handle provided by the connected
callback. Calling cyg_dropbear_done
is analogous
to the shell process exiting in a conventional ssh environment. The
second argument corresponds to the exit code of that process, so a
value of 0 indicates no error. The
cyg_dropbear_handle ceases to be valid during
the call to cyg_dropbear_done
so the application
must ensure the handle will not be used concurrently by another thread
or after the done call.
Note | |
---|---|
If |
Logging Callback
The dropbear code includes support for logging various events. In a
conventional ssh environment these log messages will end up in the
system log files, but when running under eCos that approach is usually
inappropriate. Instead by default the log messages will just be
discarded, but if desired the application can capture the messages by
installing a logging callback in the
db_logger
field of the
cyg_dropbear_data structure:
void application_logger(char* format, va_list args) { … }
Configuration
The dropbear package provides a number of additional configuration options.
-
CYGIMP_NET_DROPBEAR_SUPPORT_STDERR
- When a new secure connection is established, by default the dropbear code only provides stdin and stdout channels. If this option is enabled then application code can also request a stderr channel for certain connections. This consumes one extra file descriptor allocated during initialization, plus another two file descriptors for every connection that uses stderr.
-
CYGNUM_NET_DROPBEAR_MAX_PAYLOAD_LEN
- While establishing a secure connection the client and daemon negotiate an upper limit for the 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_MAXIMUM_PUBLIC_KEYS
- During the authentication phase the dropbear code may invoke a get_public_keys callback function provided by the application. One of the arguments to that function is a fixed-size array of pointers to public key strings. This configuration option determines the size of that array, and has a default value of 8. If for some reason the application supports large numbers of public keys then it may be necessary to increase this setting.
-
CYGNUM_NET_DROPBEAR_NETWORK_PORT
-
By default the dropbear main thread listens on port 22, the standard
TCP/IP port for ssh. An alternative network port can be specified if
desired. This provides a rather limited amount of protection against
network attacks which attempt to connect directly to the ssh port,
but will be of no use against a determined attack. Using a
non-standard port will also be an inconvenience on the client side:
the user has to either specify the port number on the command line or
modify a configuration file such as
~/.ssh/config
. -
CYGNUM_NET_DROPBEAR_LOCAL_PORT
- This configuration option controls the TCP/IP port used to establish the local socket for each ssh connection.
-
CYGNUM_NET_DROPBEAR_STDERR_PORT
-
This configuration option controls the TCP/IP port used to establish
the stderr socket during a call to
cyg_dropbear_get_stderr
. -
CYGNUM_NET_DROPBEAR_THREAD_PRI
-
The dropbear package involves one thread started by
cyg_dropbear_init
plus additional worker threads, one per ssh connection. These threads all 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 the main dropbear thread and for each worker thread. 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.
-
CYGNUM_NET_DROPBEAR_CONNECTION_DELAY
- The main dropbear thread can delay for a configurable number of system clock ticks between accepting each ssh connection from the network. This provides a limited amount of protection against denial of service attacks, especially when used in conjunction with an accept_connection callback function which limits the number of open connections. Obviously if the eCos system is the target of a denial of service attack then it will still be very hard for legitimate users to get ssh access to the unit, but there is an increased chance that the unit will continue operating rather than run out of resources and fail.
-
CYGNUM_NET_DROPBEAR_RETRY_DELAY
-
Within the main dropbear code, if the system runs out of resources and
for example a
malloc
call fails then this is treated as a serious failure and the ssh connection should get shut down, which will release resources back to the system. Where appropriate some of the eCos-specific code will instead sleep for a while and then retry, in the hope that other parts of the system have released sufficient resources during the delay. This configuration option specifies the number of system clock ticks that should be spent sleeping before retrying.
A further three configuration options in other packages may have a
significant impact on the behaviour of the dropbear code. The dropbear
code involves a fixed overhead of two sockets, one for the network
port and one for the local port, plus an additional three sockets for
each ssh connection, one for the network side and two locally. If
stderr support is enabled then there is an additional fixed overhead
of another socket plus another two sockets for each connection.
Depending on how many concurrent ssh connections the application is
expected to support it may be necessary to increase the limits on the
number of file descriptors and sockets, controlled by
CYGNUM_FILEIO_NFILE
, CYGNUM_FILEIO_NFD
and
CYGPKG_NET_MAXSOCKETS
. If C library
stdio functions are used then FOPEN_MAX
may also
need to be increased. If the system runs out of sockets or file
descriptors when a client attempts to establish a new connection,
this will normally be detected and the connection will be shut down
immediately. However there are edge and race conditions which may
cause the client to hang until a timeout occurs.
2024-03-18 | eCosPro License |