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]Note

If cyg_dropbear_done is not called when closing down an ssh connection then the dropbear worker thread associated with that connection will never terminate, and any resources allocated will not be released. If this happens repeatedly then the system will run out of memory and fail.

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.