libssh code audit
=================
Focus
-----
* Check the state machine and all transitions
* Note: The state machine is the same for the client and the server
* We had a CVE in this area last year: https://www.libssh.org/security/advisories/CVE-2018-10933.txt
* Check input sanitization (both from function parameters and configuration files)
* config file parsing (`src/config.c`)
* ssh key parsing (`src/pki_container*.c`)
* known_hosts file parsing (`src/knownhosts.c`)
Security process
----------------
https://www.libssh.org/development/security-process/
Examples
--------
Server: `examples/ssh_server_fork.c`
Client: `examples/ssh_client.c`
Fuzzing
-------
See `tests/fuzz`
https://github.com/google/oss-fuzz/tree/master/projects/libssh
Currently only the first state of the state machine after connect is fuzzed. We miss inputs (starting points) for oss-fuzz.
For this libssh would need to be extened that in src/socket.c the packets are also written to a file (client and server).
https://gitlab.com/libssh/libssh-mirror/merge_requests/59
We need a client and server similar to the fuzzer so we can create traces.
Example:
https://gitlab.com/gnutls/gnutls/blob/master/fuzz/README-adding-traces.md
Those traces should be feeded into American Fuzzy Loop so it creates variation of those inputs (needs to run for 2 days).
Communication
-------------
IRC: #libssh-pentest @ Freenode
People to talk to:
* Andreas Schneider
* IRC: gladiac
* Matrix: @asn:matrix.org
* Jakub Jelen
* IRC: jjelen
* Anderson Sasaki
* IRC: ansasaki
* Aris Adamantiadis
* IRC: ar1s
Schedule
--------
Possibly CW39 & CW40
State machine:
--------------
The state machine is not implemented in a single file, but it is scattered in the code, which makes it hard to find the exact global state. It is basically a composition of various state machines.
The global state is composed by the session state (``session->session_state``) and the states of the other contexts (``session->socket_state``, ``session->dh_handshake_state``, ``session->auth.state``, ``session->global_req_state``, ``channel->state``, etc.).
When a state machine is started, the callbacks and an initial state are set. These callbacks usually change the ``session->pending_call_state`` to the current operation being performed, which is expected to be called again in non-blocking sessions. Then a message is sent to the server and a reaction from the server is expected. The packets from the server are captured by the socket polling, which calls the appropriate callbacks for each message type (see ``packet.c``). These callbacks change the global state by changing the session state or the state of one of the other contexts.
The incoming packets are filtered (in ``ssh_packet_incoming_filter()`` in ``packet.c``) by the expected global state before being processed (by ``ssh_packet_process()``). Although not complete, it is possible to see the majority of the expected transitions made by the callbacks, documented in comments. In the beginning of the ``packet.c`` file, it is possible to see the default callbacks called for each message type.
If the session is non-blocking, it is expected the API to be called many times, while the return is ``SSH_AGAIN``. If the session is blocking, the call will block in a loop.
For example to connect to a host as a client:
* Call ``ssh_connect()``, which sets the callback as ``ssh_client_connection_callback``, the session state to ``SSH_SESSION_STATE_CONNECTING``, and pending_call_state to ``PENDING_CALL_CONNECT``
* This will call ``ssh_handle_packets()``, which will poll the socket and call the appropriate callbacks for each packet arriving
* The session state changes to ``SSH_SESSION_STATE_SOCKET_CONNECTED`` when the socket connection is stablished
* The client sends the banner. Then it expects the server to send its banner
* The server sends its banner. The polling gets the packet and calls ``callback_receive_banner()`` setting the state to ``SSH_SESSION_BANNER_RECEIVED``
* The client sends the initial key exchange info and sets the session state as ``SSH_SESSION_STATE_INITIAL_KEX``
* The polling gets the server packet with its initial key exchange info; the client changes the state to ``SSH_SESSION_STATE_KEXINIT_RECEIVED``
* The client selects the key exchange methods, sets the session state as ``SSH_SESSION_STATE_DH``, and starts the key exchange (a.k.a DH) state machine by calling ``dh_handshake()``
* The session state does not change until the state machine handling the key exchange is finished, which is informed by the ``session->dh_handshake_state``
* When the key exchange is finished, the state is changed to ``SSH_SESSION_AUTHENTICATING``.
* Again, this doesn't change until the state machine handling the authentication finishes, setting the ``session->session_state`` to ``SSH_SESSION_AUTHENTICATED`` or ``SSH_SESSION_STATE_ERROR``
Now the key exchange is finished and the user can use one of the authentication methods to authenticate.
###### tags: `libssh`