SSH2:Overview

From Libssh2

The SSH2 Protocol

The SSH2 Protocol consists of a Transport Layer which manages packet exchange between two hosts. This transport protocol is currently defined to carry an Authentication Protocol and a Connection Protocol.

Upon connection, an SSH2 client and an SSH2 server will attempt to negotiate a common set of parameters for use by the transport layer:

  • Key Exchange(kex): The cipher and message authentication keys must be negotiated such that each end is able to derive the same key, while ensuring that no intermediate parties are able to discern the shared secrets using simple network monitoring. Typically keys are exchanged using the Diffie-Hellman protocol.
  • Host Key(hostkey): In order to ensure the identity of the remote computer, the server will sign a unique token which the client may verify. The negotiated hostkey determines what algorithm (i.e. ssh-rsa or ssh-dsa) will be used.
  • Client-to-Server parameters(CS Params)
    • Ciper(CS cipher): Symmetric encryption algorithm to be used for client-to-server communications.
    • MAC(CS MAC): Message Authentication Code to be used for client-to-server communications.
    • Compression(CS Comp): Compression sheme to use for client-to-server payloads.
    • Language(CS Lang): Characterset encoding for client-to-server communications (typically unimplemented).
  • Server-to-Client parameters(SC Params)
    • Ciper(SC cipher): Symmetric encryption algorithm to be used for server-to-client communications.
    • MAC(SC MAC): Message Authentication Code to be used for server-to-client communications.
    • Compression(SC Comp): Compression sheme to use for server-to-client payloads.
    • Language(SC Lang): Characterset encoding for server-to-client communications (typically unimplemented).

Once the transport layer has been initialized, the server typically (but not necessarily) requires the client to validate its identity using the SSH2 Authentication protocol. This generally involves one of the these methods:

  • Password: Basic username and password combination encrypted by the transport layer and sent directly to the server.
  • Public Key: Public Key authentication using one of the commonly supported hostkey types. Client signs a unique token and provides it to the server for validation against a pre-known public key.
  • Host-based Public Key: Similar to normal public key authentication, except that the private key used is not unique to an individual. In this case the private key is owned by the client machine's root user and is used for host-to-host trusted logins.
  • Keyboard Interactive: A generalized API for message exchange between a backend password management system and the client. The format and expected results are implementation specific and typically require a human's interaction; Hence the name "keyboard interactive".
  • none: Typically insufficient for actual authentication. This method is often used as a known-failure to request that the server advertise what authentication methods it supports.

Upon completed authentication, the client may request any number of Connections (Data channels). Each channel may be of an independent type accessing an indepenent remote resource on the server. Connection types include:

  • Interactive Shell(shell): This is the type most commonly used by command line SSH clients for remote login.
  • Execute(exec): Execute a program on the remote system and attach to its stdio streams.
  • Subsystem(subsystem): Execute a standard SSH2 Subsystem such as SFTP or PublicKey management.

Multiple connection channels may be active within a single Transport. Communication semantics vary depending on connection type and specific service requested.

libssh2 Implementation

Connecting to a remote SSH2 server using libssh2 requires that the calling application setup a standard socket descriptor to the remote host, initialize a LIBSSH2_SESSION object, and instruct the library to begin a transport session on the opened socket.

#include <libssh2.h>

int connect_to_host(const char *hostname, int port)
{
    /* Connect to remote host using normal socket operations */
    return localSocketVar;
}

int main(void)
{
    int sock;
    LIBSSH2_SESSION *session;

    /* Connect */
    sock = connect_to_host("shell.example.com", 22);
    session = libssh2_session_init();
    if (libssh2_session_startup(session, sock)) {
        /* Connection failure */
    } else {
        /* Connection success */
    }

    ...

Assuming the connection succeeded, the session variable is now ready to authenticate...

    ...

    /* Authenticate */
    if (libssh2_userauth_password(session, "jdoe", "secret")) {
        /* Authentication failure */
    } else {
        /* Authentication success */
    }

   ...

Once successfully authenticated, any number of channels may be opened:

    ...

    if (!(channel = libssh2_channel_open_session(session))) {
        /* Failure opening channel */
    } else {
        /* Success, launch a shell in this channel */
        libssh2_channel_shell(channel);
    }

    ...

Data may then be read from or written to this (or other) channels using stdio type calls.

    ...

    /* "Type" exit on the remote shell and "press" enter */
    libssh2_channel_write(channel, "exit\n", strlen("exit\n"));

    /* Close the channel now that we're done */
    libssh2_channel_close(channel);

    /* And free its resources */
    libssh2_channel_free(channel);

    ...

After closing all channels, the session may be disconnected and freed as well:

    ...

    libssh2_session_disconnect(session, "Goodbye");
    libssh2_session_free(session);

    return 0;
}