|
|
|
@ -1,843 +0,0 @@
|
|
|
|
|
/**
|
|
|
|
|
* @page tutorial The Tutorial
|
|
|
|
|
*
|
|
|
|
|
* @section introduction Introduction
|
|
|
|
|
*
|
|
|
|
|
* libssh is a C library that enables you to write a program that uses the
|
|
|
|
|
* SSH protocol. With it, you can remotely execute programs, transfer
|
|
|
|
|
* files, or use a secure and transparent tunnel for your remote programs.
|
|
|
|
|
* The SSH protocol is encrypted, ensures data integrity, and provides strong
|
|
|
|
|
* means of authenticating both the server of the client. The library hides
|
|
|
|
|
* a lot of technical details from the SSH protocol, but this does not
|
|
|
|
|
* mean that you should not try to know about and understand these details.
|
|
|
|
|
*
|
|
|
|
|
* libssh is a Free Software / Open Source project. The libssh library
|
|
|
|
|
* is distributed under LGPL license. The libssh project has nothing to do with
|
|
|
|
|
* "libssh2", which is a completly different and independant project.
|
|
|
|
|
*
|
|
|
|
|
* libssh supports both client and server sides of the SSH protocol.
|
|
|
|
|
* The following document explains how to set up a client-side connection.
|
|
|
|
|
* If you are going to program a server, you should try first to code some
|
|
|
|
|
* client-side programs in order to understand how libssh works.
|
|
|
|
|
*
|
|
|
|
|
* This tutorial describes libssh version 0.5.0.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Table of contents:
|
|
|
|
|
*
|
|
|
|
|
* @subpage session
|
|
|
|
|
*
|
|
|
|
|
* @subpage details
|
|
|
|
|
*
|
|
|
|
|
* @subpage tbd
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @page session Chapter 1: A typical SSH session
|
|
|
|
|
* @section ssh_session A typical SSH session
|
|
|
|
|
*
|
|
|
|
|
* A SSH session goes through the following steps:
|
|
|
|
|
*
|
|
|
|
|
* - Before connecting to the server, you can set up if you wish one or other
|
|
|
|
|
* server public key authentication, i.e. DSA or RSA. You can choose
|
|
|
|
|
* cryptographic algorithms you trust and compression algorithms if any. You
|
|
|
|
|
* must of course set up the hostname.
|
|
|
|
|
*
|
|
|
|
|
* - The connection is established. A secure handshake is made, and resulting from
|
|
|
|
|
* it, a public key from the server is gained. You MUST verify that the public
|
|
|
|
|
* key is legitimate, using for instance the MD5 fingerprint or the known hosts
|
|
|
|
|
* file.
|
|
|
|
|
*
|
|
|
|
|
* - The client must authenticate: the classical ways are password, or
|
|
|
|
|
* public keys (from dsa and rsa key-pairs generated by openssh).
|
|
|
|
|
* If a SSH agent is running, it is possible to use it.
|
|
|
|
|
*
|
|
|
|
|
* - Now that the user has been authenticated, you must open one or several
|
|
|
|
|
* channels. Channels are different subways for information into a single ssh
|
|
|
|
|
* connection. Each channel has a standard stream (stdout) and an error stream
|
|
|
|
|
* (stderr). You can theoretically open an infinity of channels.
|
|
|
|
|
*
|
|
|
|
|
* - With the channel you opened, you can do several things:
|
|
|
|
|
* - Execute a single command.
|
|
|
|
|
* - Open a shell. You may want to request a pseudo virtual terminal before.
|
|
|
|
|
* - Invoke the sftp subsystem to transfer files.
|
|
|
|
|
* - Invoke the scp subsystem to transfer files.
|
|
|
|
|
* - Invoke your own subsystem. This is outside the scope of this document,
|
|
|
|
|
* but it is easy to do.
|
|
|
|
|
*
|
|
|
|
|
* - When everything is finished, just close the channels, and then the connection.
|
|
|
|
|
*
|
|
|
|
|
* All the libssh functions which return an error code also set an error message.
|
|
|
|
|
* Error codes are typically SSH_ERROR for integer values, or NULL for pointers.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection setup Creating the session and setting options
|
|
|
|
|
*
|
|
|
|
|
* The most important object in a SSH connection is the SSH session. In order
|
|
|
|
|
* to allocate a new SSH session, you use ssh_new(). Don't forget to
|
|
|
|
|
* always verify that the allocation successed.
|
|
|
|
|
* @code
|
|
|
|
|
* #include <libssh/libssh.h>
|
|
|
|
|
* #include <stdlib.h>
|
|
|
|
|
*
|
|
|
|
|
* int main()
|
|
|
|
|
* {
|
|
|
|
|
* ssh_session my_ssh_session = ssh_new();
|
|
|
|
|
* if (my_ssh_session == NULL)
|
|
|
|
|
* exit(-1);
|
|
|
|
|
* ...
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* libssh follows the allocate-it-deallocate-it pattern. Each object that you allocate
|
|
|
|
|
* using xxxxx_new() must be deallocated using xxxxx_free(). In this case, ssh_new()
|
|
|
|
|
* does the allocation and ssh_free() does the contrary.
|
|
|
|
|
*
|
|
|
|
|
* The ssh_options_set() function sets the options of the session. The most important options are:
|
|
|
|
|
* - SSH_OPTIONS_HOST: the name of the host you want to connect to
|
|
|
|
|
* - SSH_OPTIONS_PORT: the used port (default is port 22)
|
|
|
|
|
* - SSH_OPTIONS_USER: the system user under which you want to connect
|
|
|
|
|
* - SSH_OPTIONS_LOG_VERBOSITY: the quantity of messages that are printed
|
|
|
|
|
*
|
|
|
|
|
* The complete list of options can be found in the documentation of ssh_options_set().
|
|
|
|
|
* The only mandatory option is SSH_OPTIONS_HOST. If you don't use SSH_OPTIONS_USER,
|
|
|
|
|
* the local username of your account will be used.
|
|
|
|
|
*
|
|
|
|
|
* Here is a small example of how to use it:
|
|
|
|
|
* @code
|
|
|
|
|
* #include <libssh/libssh.h>
|
|
|
|
|
* #include <stdlib.h>
|
|
|
|
|
*
|
|
|
|
|
* int main()
|
|
|
|
|
* {
|
|
|
|
|
* ssh_session my_ssh_session;
|
|
|
|
|
* int verbosity = SSH_LOG_PROTOCOL;
|
|
|
|
|
* int port = 22;
|
|
|
|
|
*
|
|
|
|
|
* my_ssh_session = ssh_new();
|
|
|
|
|
* if (my_ssh_session == NULL)
|
|
|
|
|
* exit(-1);
|
|
|
|
|
*
|
|
|
|
|
* ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost");
|
|
|
|
|
* ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
|
|
|
|
* ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &port);
|
|
|
|
|
*
|
|
|
|
|
* ...
|
|
|
|
|
*
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* Please notice that all parameters are passed to ssh_options_set() as pointers,
|
|
|
|
|
* even if you need to set an integer value.
|
|
|
|
|
*
|
|
|
|
|
* @see ssh_new
|
|
|
|
|
* @see ssh_free
|
|
|
|
|
* @see ssh_options_set
|
|
|
|
|
* @see ssh_options_parse_config
|
|
|
|
|
* @see ssh_options_copy
|
|
|
|
|
* @see ssh_options_getopt
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection connect Connecting to the server
|
|
|
|
|
*
|
|
|
|
|
* Once all settings have been made, you can connect using ssh_connect(). That
|
|
|
|
|
* function will return SSH_OK if the connection worked, SSH_ERROR otherwise.
|
|
|
|
|
*
|
|
|
|
|
* You can get the error string using ssh_get_error() in order to show the
|
|
|
|
|
* user what went wrong. Then, use ssh_disconnect() when you want to stop
|
|
|
|
|
* the session.
|
|
|
|
|
*
|
|
|
|
|
* Here's an example:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* #include <libssh/libssh.h>
|
|
|
|
|
* #include <stdlib.h>
|
|
|
|
|
* #include <stdio.h>
|
|
|
|
|
*
|
|
|
|
|
* int main()
|
|
|
|
|
* {
|
|
|
|
|
* ssh_session my_ssh_session;
|
|
|
|
|
* int ret;
|
|
|
|
|
*
|
|
|
|
|
* my_ssh_session = ssh_new();
|
|
|
|
|
* if (my_ssh_session == NULL)
|
|
|
|
|
* exit(-1);
|
|
|
|
|
*
|
|
|
|
|
* ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost");
|
|
|
|
|
*
|
|
|
|
|
* ret = ssh_connect(my_ssh_session);
|
|
|
|
|
* if (ret != SSH_OK)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Error connecting to localhost: %s\n",
|
|
|
|
|
* ssh_get_error(my_ssh_session));
|
|
|
|
|
* exit(-1);
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* ...
|
|
|
|
|
*
|
|
|
|
|
* ssh_disconnect(my_ssh_session);
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection serverauth Authenticating the server
|
|
|
|
|
*
|
|
|
|
|
* Once you're connected, the following step is mandatory: you must check that the server
|
|
|
|
|
* you just connected to is known and safe to use (remember, SSH is about security and
|
|
|
|
|
* authentication).
|
|
|
|
|
*
|
|
|
|
|
* There are two ways of doing this:
|
|
|
|
|
* - The first way (recommended) is to use the ssh_is_server_known()
|
|
|
|
|
* function. This function will look into the known host file
|
|
|
|
|
* (~/.ssh/known_hosts on UNIX), look for the server hostname's pattern,
|
|
|
|
|
* and determine whether this host is present or not in the list.
|
|
|
|
|
* - The second way is to use ssh_get_pubkey_hash() to get a binary version
|
|
|
|
|
* of the public key hash value. You can then use your own database to check
|
|
|
|
|
* if this public key is known and secure.
|
|
|
|
|
*
|
|
|
|
|
* You can also use the ssh_get_pubkey_hash() to show the public key hash
|
|
|
|
|
* value to the user, in case he knows what the public key hash value is
|
|
|
|
|
* (some paranoid people write their public key hash values on paper before
|
|
|
|
|
* going abroad, just in case ...).
|
|
|
|
|
*
|
|
|
|
|
* If the remote host is being used to for the first time, you can ask the user whether
|
|
|
|
|
* he/she trusts it. Once he/she concluded that the host is valid and worth being
|
|
|
|
|
* added in the known hosts file, you use ssh_write_knownhost() to register it in
|
|
|
|
|
* the known hosts file, or any other way if you use your own database.
|
|
|
|
|
*
|
|
|
|
|
* The following example is part of the examples suite available in the
|
|
|
|
|
* examples/ directory:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* int verify_knownhost(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* int state, hlen;
|
|
|
|
|
* unsigned char *hash = NULL;
|
|
|
|
|
* char *hexa;
|
|
|
|
|
* char buf[10];
|
|
|
|
|
*
|
|
|
|
|
* state = ssh_is_server_known(session);
|
|
|
|
|
*
|
|
|
|
|
* hlen = ssh_get_pubkey_hash(session, &hash);
|
|
|
|
|
* if (hlen < 0)
|
|
|
|
|
* return -1;
|
|
|
|
|
*
|
|
|
|
|
* switch (state)
|
|
|
|
|
* {
|
|
|
|
|
* case SSH_SERVER_KNOWN_OK:
|
|
|
|
|
* break; /* ok */
|
|
|
|
|
*
|
|
|
|
|
* case SSH_SERVER_KNOWN_CHANGED:
|
|
|
|
|
* fprintf(stderr, "Host key for server changed: it is now:\n");
|
|
|
|
|
* ssh_print_hexa("Public key hash", hash, hlen);
|
|
|
|
|
* fprintf(stderr, "For security reasons, connection will be stopped\n");
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return -1;
|
|
|
|
|
*
|
|
|
|
|
* case SSH_SERVER_FOUND_OTHER:
|
|
|
|
|
* fprintf(stderr, "The host key for this server was not found but an other"
|
|
|
|
|
* "type of key exists.\n");
|
|
|
|
|
* fprintf(stderr, "An attacker might change the default server key to"
|
|
|
|
|
* "confuse your client into thinking the key does not exist\n"
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return -1;
|
|
|
|
|
*
|
|
|
|
|
* case SSH_SERVER_FILE_NOT_FOUND:
|
|
|
|
|
* fprintf(stderr, "Could not find known host file.\n");
|
|
|
|
|
* fprintf(stderr, "If you accept the host key here, the file will be"
|
|
|
|
|
* "automatically created.\n");
|
|
|
|
|
* /* fallback to SSH_SERVER_NOT_KNOWN behavior */
|
|
|
|
|
*
|
|
|
|
|
* case SSH_SERVER_NOT_KNOWN:
|
|
|
|
|
* hexa = ssh_get_hexa(hash, hlen);
|
|
|
|
|
* fprintf(stderr,"The server is unknown. Do you trust the host key?\n");
|
|
|
|
|
* fprintf(stderr, "Public key hash: %s\n", hexa);
|
|
|
|
|
* free(hexa);
|
|
|
|
|
* if (fgets(buf, sizeof(buf), stdin) == NULL)
|
|
|
|
|
* {
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return -1;
|
|
|
|
|
* }
|
|
|
|
|
* if (strncasecmp(buf, "yes", 3) != 0)
|
|
|
|
|
* {
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return -1;
|
|
|
|
|
* }
|
|
|
|
|
* if (ssh_write_knownhost(session) < 0)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Error %s\n", strerror(errno));
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return -1;
|
|
|
|
|
* }
|
|
|
|
|
* break;
|
|
|
|
|
*
|
|
|
|
|
* case SSH_SERVER_ERROR:
|
|
|
|
|
* fprintf(stderr, "Error %s", ssh_get_error(session));
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return -1;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* free(hash);
|
|
|
|
|
* return 0;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see ssh_connect
|
|
|
|
|
* @see ssh_disconnect
|
|
|
|
|
* @see ssh_get_error
|
|
|
|
|
* @see ssh_get_error_code
|
|
|
|
|
* @see ssh_get_pubkey_hash
|
|
|
|
|
* @see ssh_is_server_known
|
|
|
|
|
* @see ssh_write_knownhost
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection auth Authenticating yourself
|
|
|
|
|
*
|
|
|
|
|
* The authentication process is the way a service provider can identify a
|
|
|
|
|
* user and verify his/her identity. The authorization process is about enabling
|
|
|
|
|
* the authenticated user the access to ressources. In SSH, the two concepts
|
|
|
|
|
* are linked. After authentication, the server can grant the user access to
|
|
|
|
|
* several ressources such as port forwarding, shell, sftp subsystem, and so on.
|
|
|
|
|
*
|
|
|
|
|
* libssh supports several methods of authentication:
|
|
|
|
|
* - "none" method. This method allows to get the available authentications
|
|
|
|
|
* methods. It also gives the server a chance to authenticate the user with
|
|
|
|
|
* just his/her login. Some very old hardware uses this feature to fallback
|
|
|
|
|
* the user on a "telnet over SSH" style of login.
|
|
|
|
|
* - password method. A password is sent to the server, which accepts it or not.
|
|
|
|
|
* - keyboard-interactive method. The server sends several challenges to the
|
|
|
|
|
* user, who must answer correctly. This makes possible the authentication
|
|
|
|
|
* via a codebook for instance ("give code at 23:R on page 3").
|
|
|
|
|
* - public key method. The host knows the public key of the user, and the
|
|
|
|
|
* user must prove he knows the associated private key. This can be done
|
|
|
|
|
* manually, or delegated to the SSH agent as we'll see later.
|
|
|
|
|
*
|
|
|
|
|
* All these methods can be combined. You can for instance force the user to
|
|
|
|
|
* authenticate with at least two of the authentication methods. In that case,
|
|
|
|
|
* one speaks of "Partial authentication". A partial authentication is a
|
|
|
|
|
* response from authentication functions stating that your credential was
|
|
|
|
|
* accepted, but yet another one is required to get in.
|
|
|
|
|
*
|
|
|
|
|
* The example below shows an authentication with password:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* #include <libssh/libssh.h>
|
|
|
|
|
* #include <stdlib.h>
|
|
|
|
|
* #include <stdio.h>
|
|
|
|
|
*
|
|
|
|
|
* int main()
|
|
|
|
|
* {
|
|
|
|
|
* ssh_session my_ssh_session;
|
|
|
|
|
* int ret;
|
|
|
|
|
* char *password;
|
|
|
|
|
*
|
|
|
|
|
* // Open session and set options
|
|
|
|
|
* my_ssh_session = ssh_new();
|
|
|
|
|
* if (my_ssh_session == NULL)
|
|
|
|
|
* exit(-1);
|
|
|
|
|
* ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost");
|
|
|
|
|
*
|
|
|
|
|
* // Connect to server
|
|
|
|
|
* ret = ssh_connect(my_ssh_session);
|
|
|
|
|
* if (ret != SSH_OK)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Error connecting to localhost: %s\n",
|
|
|
|
|
* ssh_get_error(my_ssh_session));
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* exit(-1);
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* // Verify the server's identity
|
|
|
|
|
* // For the source code of verify_knowhost(), check previous example
|
|
|
|
|
* if (verify_knownhost(my_ssh_session) < 0)
|
|
|
|
|
* {
|
|
|
|
|
* ssh_disconnect(my_ssh_session);
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* exit(-1);
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* // Authenticate ourselves
|
|
|
|
|
* password = getpass("Password: ");
|
|
|
|
|
* ret = ssh_userauth_password(my_ssh_session, NULL, password);
|
|
|
|
|
* if (ret != SSH_AUTH_SUCCESS)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Error authenticating with password: %s\n",
|
|
|
|
|
* ssh_get_error(my_ssh_session));
|
|
|
|
|
* ssh_disconnect(my_ssh_session);
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* exit(-1);
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* ...
|
|
|
|
|
*
|
|
|
|
|
* ssh_disconnect(my_ssh_session);
|
|
|
|
|
* ssh_free(my_ssh_session);
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see @ref authentication_details
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection using_ssh Doing something
|
|
|
|
|
*
|
|
|
|
|
* At this point, the authenticity of both server and client is established.
|
|
|
|
|
* Time has come to take advantage of the many possibilities offered by the SSH
|
|
|
|
|
* protocol: execute remote commands, open remote shells, transfer files,
|
|
|
|
|
* forward ports, etc.
|
|
|
|
|
*
|
|
|
|
|
* The example below puts the final touch to our step-by-step discovery
|
|
|
|
|
* of a SSH connection:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* /* *** To be replaced with something simpler *** */
|
|
|
|
|
* #include <sys/stat.h>
|
|
|
|
|
*
|
|
|
|
|
* int scp_helloworld(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* ssh_scp scp_session;
|
|
|
|
|
* int ret;
|
|
|
|
|
* const char *helloworld = "Hello, world!\n";
|
|
|
|
|
* int length = strlen(helloworld);
|
|
|
|
|
*
|
|
|
|
|
* // Open a scp session
|
|
|
|
|
* scp_session = ssh_scp_new
|
|
|
|
|
* (session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, ".");
|
|
|
|
|
* if (scp_session == NULL)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Error allocating scp session.\n");
|
|
|
|
|
* return SSH_ERROR;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* // Initialize the scp session
|
|
|
|
|
* ret = ssh_scp_init(scp_session);
|
|
|
|
|
* if (ret != SSH_OK)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Error initializing scp session.\n");
|
|
|
|
|
* ssh_scp_free(scp_session);
|
|
|
|
|
* return ret;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* // Open the remote file
|
|
|
|
|
* ret = ssh_scp_push_file
|
|
|
|
|
* (scp_session, "helloworld.txt", length, S_IRUSR | S_IWUSR);
|
|
|
|
|
* if (ret != SSH_OK)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Can't open remote file.\n");
|
|
|
|
|
* ssh_scp_close(scp_session);
|
|
|
|
|
* ssh_scp_free(scp_session);
|
|
|
|
|
* return ret;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* // Write the data into the remote file
|
|
|
|
|
* ret = ssh_scp_write(scp_session, helloworld, length);
|
|
|
|
|
* if (ret != SSH_OK)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Can't write to remote file.\n");
|
|
|
|
|
* ssh_scp_close(scp_session);
|
|
|
|
|
* ssh_scp_free(scp_session);
|
|
|
|
|
* return ret;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* ssh_scp_close(scp_session);
|
|
|
|
|
* ssh_scp_free(scp_session);
|
|
|
|
|
* return ret;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see @ref opening_shell
|
|
|
|
|
* @see @ref remote_commands
|
|
|
|
|
* @see @ref sftp_subsystem
|
|
|
|
|
* @see @ref scp_subsystem
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @page details Chapter 2: A deeper insight on authentication
|
|
|
|
|
* @section authentication_details A deeper insight on authentication
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection pubkeys Authenticating using public keys
|
|
|
|
|
*
|
|
|
|
|
* The public key authentication is the only method that does not compromise
|
|
|
|
|
* your key if the remote host has been compromised (the server can't do
|
|
|
|
|
* anything more than getting your public key). This is not the
|
|
|
|
|
* case of a password authentication (the server can get your plaintext
|
|
|
|
|
* password). On the other hand, if the client machine is compromised and
|
|
|
|
|
* the private key has no passphrase, then the attacker has gained automatic
|
|
|
|
|
* access to your server. It is not the purpose of this document to do
|
|
|
|
|
* a detailed review of the advantages and drawbacks of each authentication
|
|
|
|
|
* method, so refer to the abundant documentation on this topic on the
|
|
|
|
|
* Internet to fully understand the advantages and risks linked to each method.
|
|
|
|
|
*
|
|
|
|
|
* libssh is fully compatible with the openssh public and private keys. You
|
|
|
|
|
* can either use the automatic public key authentication method provided by
|
|
|
|
|
* libssh, or roll your own using the public key functions.
|
|
|
|
|
*
|
|
|
|
|
* The process of authenticating by public key to a server is the following:
|
|
|
|
|
* - you scan a list of files that contain public keys. each key is sent to
|
|
|
|
|
* the SSH server, until the server acknowledges a key (a key it knows can be
|
|
|
|
|
* used to authenticate the user).
|
|
|
|
|
* - then, you retrieve the private key for this key and send a message
|
|
|
|
|
* proving that you know that private key.
|
|
|
|
|
*
|
|
|
|
|
* The function ssh_userauth_autopubkey() does this using the available keys in
|
|
|
|
|
* "~/.ssh/". The return values are the following:
|
|
|
|
|
* - SSH_AUTH_ERROR: some serious error happened during authentication
|
|
|
|
|
* - SSH_AUTH_DENIED: no key matched
|
|
|
|
|
* - SSH_AUTH_SUCCESS: you are now authenticated
|
|
|
|
|
* - SSH_AUTH_PARTIAL: some key matched but you still have to provide an other
|
|
|
|
|
* mean of authentication (like a password).
|
|
|
|
|
*
|
|
|
|
|
* The ssh_userauth_autopubkey function also tries to authenticate using the
|
|
|
|
|
* SSH agent, if you have one running, or the "none" method otherwise.
|
|
|
|
|
*
|
|
|
|
|
* If you wish to authenticate with public key by your own, follow these steps:
|
|
|
|
|
* - Retrieve the public key in a ssh_string using publickey_from_file().
|
|
|
|
|
* - Offer the public key to the SSH server using ssh_userauth_offer_pubkey().
|
|
|
|
|
* If the return value is SSH_AUTH_SUCCESS, the SSH server accepts to
|
|
|
|
|
* authenticate using the public key and you can go to the next step.
|
|
|
|
|
* - Retrieve the private key, using the privatekey_from_file() function. If
|
|
|
|
|
* a passphrase is needed, either the passphrase specified as argument or
|
|
|
|
|
* a callback (see callbacks section) will be used.
|
|
|
|
|
* - Authenticate using ssh_userauth_pubkey() with your public key string
|
|
|
|
|
* and private key.
|
|
|
|
|
* - Do not forget cleaning up memory using string_free() and privatekey_free().
|
|
|
|
|
*
|
|
|
|
|
* Here is a minimalistic example of public key authentication:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* int authenticate_pubkey(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* int rc;
|
|
|
|
|
*
|
|
|
|
|
* rc = ssh_userauth_autopubkey(session, NULL);
|
|
|
|
|
*
|
|
|
|
|
* if (rc == SSH_AUTH_ERROR)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Authentication failed: %s\n",
|
|
|
|
|
* ssh_get_error(session));
|
|
|
|
|
* return SSH_AUTH_ERROR;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* return rc;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see ssh_userauth_autopubkey
|
|
|
|
|
* @see ssh_userauth_offer_pubkey
|
|
|
|
|
* @see ssh_userauth_pubkey
|
|
|
|
|
* @see publickey_from_file
|
|
|
|
|
* @see publickey_from_privatekey
|
|
|
|
|
* @see string_free
|
|
|
|
|
* @see privatekey_from_file
|
|
|
|
|
* @see privatekey_free
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection password Authenticating using a password
|
|
|
|
|
*
|
|
|
|
|
* The function ssh_userauth_password() serves the purpose of authenticating
|
|
|
|
|
* using a password. It will return SSH_AUTH_SUCCESS if the password worked,
|
|
|
|
|
* or one of other constants otherwise. It's your work to ask the password
|
|
|
|
|
* and to deallocate it in a secure manner.
|
|
|
|
|
*
|
|
|
|
|
* If your server complains that the password is wrong, but you can still
|
|
|
|
|
* authenticate using openssh's client (issuing password), it's probably
|
|
|
|
|
* because openssh only accept keyboard-interactive. Switch to
|
|
|
|
|
* keyboard-interactive authentication, or try to configure plain text passwords
|
|
|
|
|
* on the SSH server.
|
|
|
|
|
*
|
|
|
|
|
* Here is a small example of password authentication:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* int authenticate_password(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* char *password;
|
|
|
|
|
* int rc;
|
|
|
|
|
*
|
|
|
|
|
* password = getpass("Enter your password: ");
|
|
|
|
|
* rc = ssh_userauth_password(session, NULL, password);
|
|
|
|
|
* if (rc == SSH_AUTH_ERROR)
|
|
|
|
|
* {
|
|
|
|
|
* fprintf(stderr, "Authentication failed: %s\n",
|
|
|
|
|
* ssh_get_error(session));
|
|
|
|
|
* return SSH_AUTH_ERROR;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* return rc;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see ssh_userauth_password
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection keyb_int The keyboard-interactive authentication method
|
|
|
|
|
*
|
|
|
|
|
* The keyboard-interactive method is, as its name tells, interactive. The
|
|
|
|
|
* server will issue one or more challenges that the user has to answer,
|
|
|
|
|
* until the server takes an authentication decision.
|
|
|
|
|
*
|
|
|
|
|
* ssh_userauth_kbdint() is the the main keyboard-interactive function.
|
|
|
|
|
* It will return SSH_AUTH_SUCCESS,SSH_AUTH_DENIED, SSH_AUTH_PARTIAL,
|
|
|
|
|
* SSH_AUTH_ERROR, or SSH_AUTH_INFO, depending on the result of the request.
|
|
|
|
|
*
|
|
|
|
|
* The keyboard-interactive authentication method of SSH2 is a feature that
|
|
|
|
|
* permits the server to ask a certain number of questions in an interactive
|
|
|
|
|
* manner to the client, until it decides to accept or deny the login.
|
|
|
|
|
*
|
|
|
|
|
* To begin, you call ssh_userauth_kbdint() (just set user and submethods to
|
|
|
|
|
* NULL) and store the answer.
|
|
|
|
|
*
|
|
|
|
|
* If the answer is SSH_AUTH_INFO, it means that the server has sent a few
|
|
|
|
|
* questions that you should ask the user. You can retrieve these questions
|
|
|
|
|
* with the following functions: ssh_userauth_kbdint_getnprompts(),
|
|
|
|
|
* ssh_userauth_kbdint_getname(), ssh_userauth_kbdint_getinstruction(), and
|
|
|
|
|
* ssh_userauth_kbdint_getprompt().
|
|
|
|
|
*
|
|
|
|
|
* Set the answer for each question in the challenge using
|
|
|
|
|
* ssh_userauth_kbdint_setanswer().
|
|
|
|
|
*
|
|
|
|
|
* Then, call again ssh_userauth_kbdint() and start the process again until
|
|
|
|
|
* these functions returns something else than SSH_AUTH_INFO.
|
|
|
|
|
*
|
|
|
|
|
* Here are a few remarks:
|
|
|
|
|
* - Even the first call can return SSH_AUTH_DENIED or SSH_AUTH_SUCCESS.
|
|
|
|
|
* - The server can send an empty question set (this is the default behavior
|
|
|
|
|
* on my system) after you have sent the answers to the first questions.
|
|
|
|
|
* You must still parse the answer, it might contain some
|
|
|
|
|
* message from the server saying hello or such things. Just call
|
|
|
|
|
* ssh_userauth_kbdint() until needed.
|
|
|
|
|
* - The meaning of "name", "prompt", "instruction" may be a little
|
|
|
|
|
* confusing. An explanation is given in the RFC section that follows.
|
|
|
|
|
*
|
|
|
|
|
* Here is a little note about how to use the information from
|
|
|
|
|
* keyboard-interactive authentication, coming from the RFC itself (rfc4256):
|
|
|
|
|
*
|
|
|
|
|
* @verbatim
|
|
|
|
|
*
|
|
|
|
|
* 3.3 User Interface Upon receiving a request message, the client SHOULD
|
|
|
|
|
* prompt the user as follows: A command line interface (CLI) client SHOULD
|
|
|
|
|
* print the name and instruction (if non-empty), adding newlines. Then for
|
|
|
|
|
* each prompt in turn, the client SHOULD display the prompt and read the
|
|
|
|
|
* user input.
|
|
|
|
|
*
|
|
|
|
|
* A graphical user interface (GUI) client has many choices on how to prompt
|
|
|
|
|
* the user. One possibility is to use the name field (possibly prefixed
|
|
|
|
|
* with the application's name) as the title of a dialog window in which
|
|
|
|
|
* the prompt(s) are presented. In that dialog window, the instruction field
|
|
|
|
|
* would be a text message, and the prompts would be labels for text entry
|
|
|
|
|
* fields. All fields SHOULD be presented to the user, for example an
|
|
|
|
|
* implementation SHOULD NOT discard the name field because its windows lack
|
|
|
|
|
* titles; it SHOULD instead find another way to display this information. If
|
|
|
|
|
* prompts are presented in a dialog window, then the client SHOULD NOT
|
|
|
|
|
* present each prompt in a separate window.
|
|
|
|
|
*
|
|
|
|
|
* All clients MUST properly handle an instruction field with embedded
|
|
|
|
|
* newlines. They SHOULD also be able to display at least 30 characters for
|
|
|
|
|
* the name and prompts. If the server presents names or prompts longer than 30
|
|
|
|
|
* characters, the client MAY truncate these fields to the length it can
|
|
|
|
|
* display. If the client does truncate any fields, there MUST be an obvious
|
|
|
|
|
* indication that such truncation has occured.
|
|
|
|
|
*
|
|
|
|
|
* The instruction field SHOULD NOT be truncated. Clients SHOULD use control
|
|
|
|
|
* character filtering as discussed in [SSH-ARCH] to avoid attacks by
|
|
|
|
|
* including terminal control characters in the fields to be displayed.
|
|
|
|
|
*
|
|
|
|
|
* For each prompt, the corresponding echo field indicates whether or not
|
|
|
|
|
* the user input should be echoed as characters are typed. Clients SHOULD
|
|
|
|
|
* correctly echo/mask user input for each prompt independently of other
|
|
|
|
|
* prompts in the request message. If a client does not honor the echo field
|
|
|
|
|
* for whatever reason, then the client MUST err on the side of
|
|
|
|
|
* masking input. A GUI client might like to have a checkbox toggling
|
|
|
|
|
* echo/mask. Clients SHOULD NOT add any additional characters to the prompt
|
|
|
|
|
* such as ": " (colon-space); the server is responsible for supplying all
|
|
|
|
|
* text to be displayed to the user. Clients MUST also accept empty responses
|
|
|
|
|
* from the user and pass them on as empty strings.
|
|
|
|
|
* @endverbatim
|
|
|
|
|
*
|
|
|
|
|
* The following example shows how to perform keyboard-interactive authentication:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* int authenticate_kbdint(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* int rc;
|
|
|
|
|
*
|
|
|
|
|
* rc = ssh_userauth_kbdint(session, NULL, NULL);
|
|
|
|
|
* while (rc == SSH_AUTH_INFO)
|
|
|
|
|
* {
|
|
|
|
|
* const char *name, *instruction;
|
|
|
|
|
* int nprompts, iprompt;
|
|
|
|
|
*
|
|
|
|
|
* name = ssh_userauth_kbdint_getname(session);
|
|
|
|
|
* instruction = ssh_userauth_kbdint_getinstruction(session);
|
|
|
|
|
* nprompts = ssh_userauth_kbdint_getnprompts(session);
|
|
|
|
|
*
|
|
|
|
|
* if (strlen(name) > 0)
|
|
|
|
|
* printf("%s\n", name);
|
|
|
|
|
* if (strlen(instruction) > 0)
|
|
|
|
|
* printf("%s\n", instruction);
|
|
|
|
|
* for (iprompt = 0; iprompt < nprompts; iprompt++)
|
|
|
|
|
* {
|
|
|
|
|
* const char *prompt;
|
|
|
|
|
* char echo;
|
|
|
|
|
*
|
|
|
|
|
* prompt = ssh_userauth_kbdint_getprompt(session, iprompt, &echo);
|
|
|
|
|
* if (echo)
|
|
|
|
|
* {
|
|
|
|
|
* char buffer[128], *ptr;
|
|
|
|
|
*
|
|
|
|
|
* printf("%s", prompt);
|
|
|
|
|
* if (fgets(buffer, sizeof(buffer), stdin) == NULL)
|
|
|
|
|
* return SSH_AUTH_ERROR;
|
|
|
|
|
* buffer[sizeof(buffer) - 1] = '\0';
|
|
|
|
|
* if ((ptr = strchr(buffer, '\n')) != NULL)
|
|
|
|
|
* *ptr = '\0';
|
|
|
|
|
* if (ssh_userauth_kbdint_setanswer(session, iprompt, buffer) < 0)
|
|
|
|
|
* return SSH_AUTH_ERROR;
|
|
|
|
|
* memset(buffer, 0, strlen(buffer));
|
|
|
|
|
* }
|
|
|
|
|
* else
|
|
|
|
|
* {
|
|
|
|
|
* char *ptr;
|
|
|
|
|
*
|
|
|
|
|
* ptr = getpass(prompt);
|
|
|
|
|
* if (ssh_userauth_kbdint_setanswer(session, iprompt, ptr) < 0)
|
|
|
|
|
* return SSH_AUTH_ERROR;
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* rc = ssh_userauth_kbdint(session, NULL, NULL);
|
|
|
|
|
* }
|
|
|
|
|
* return rc;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see ssh_userauth_kbdint()
|
|
|
|
|
* @see ssh_userauth_kbdint_getnprompts
|
|
|
|
|
* @see ssh_userauth_kbdint_getname
|
|
|
|
|
* @see ssh_userauth_kbdint_getinstruction
|
|
|
|
|
* @see ssh_userauth_kbdint_getprompt
|
|
|
|
|
* @see ssh_userauth_kbdint_setanswer()
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection none Authenticating with "none" method
|
|
|
|
|
*
|
|
|
|
|
* The primary purpose of the "none" method is to get authenticated **without**
|
|
|
|
|
* any credential. Don't do that, use one of the other authentication methods,
|
|
|
|
|
* unless you really want to grant anonymous access.
|
|
|
|
|
*
|
|
|
|
|
* If the account has no password, and if the server is configured to let you
|
|
|
|
|
* pass, ssh_userauth_none() might answer SSH_AUTH_SUCCESS.
|
|
|
|
|
*
|
|
|
|
|
* The following example shows how to perform "none" authentication:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* int authenticate_kbdint(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* int rc;
|
|
|
|
|
*
|
|
|
|
|
* rc = ssh_userauth_none(session, NULL, NULL);
|
|
|
|
|
* return rc;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @subsection auth_list Getting the list of supported authentications
|
|
|
|
|
*
|
|
|
|
|
* You are not meant to choose a given authentication method, you can
|
|
|
|
|
* let the server tell you which methods are available. Once you know them,
|
|
|
|
|
* you try them one after the other.
|
|
|
|
|
*
|
|
|
|
|
* The following example shows how to get the list of available authentication
|
|
|
|
|
* methods with ssh_userauth_list() and how to use the result:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* /* Marche pas sans ssh_userauth_none(), du moins en 0.4.2 */
|
|
|
|
|
* int test_several_auth_methods(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* int method, rc;
|
|
|
|
|
*
|
|
|
|
|
* method = ssh_userauth_list(session, NULL);
|
|
|
|
|
*
|
|
|
|
|
* if (method & SSH_AUTH_METHOD_NONE)
|
|
|
|
|
* { // For the source code of function authenticate_none(),
|
|
|
|
|
* // refer to the corresponding example
|
|
|
|
|
* rc = authenticate_none(session);
|
|
|
|
|
* if (rc == SSH_AUTH_SUCCESS) return rc;
|
|
|
|
|
* }
|
|
|
|
|
* if (method & SSH_AUTH_METHOD_PUBLICKEY)
|
|
|
|
|
* { // For the source code of function authenticate_pubkey(),
|
|
|
|
|
* // refer to the corresponding example
|
|
|
|
|
* rc = authenticate_pubkey(session);
|
|
|
|
|
* if (rc == SSH_AUTH_SUCCESS) return rc;
|
|
|
|
|
* }
|
|
|
|
|
* if (method & SSH_AUTH_METHOD_INTERACTIVE)
|
|
|
|
|
* { // For the source code of function authenticate_kbdint(),
|
|
|
|
|
* // refer to the corresponding example
|
|
|
|
|
* rc = authenticate_kbdint(session);
|
|
|
|
|
* if (rc == SSH_AUTH_SUCCESS) return rc;
|
|
|
|
|
* }
|
|
|
|
|
* if (method & SSH_AUTH_METHOD_PASSWORD)
|
|
|
|
|
* { // For the source code of function authenticate_password(),
|
|
|
|
|
* // refer to the corresponding example
|
|
|
|
|
* rc = authenticate_password(session);
|
|
|
|
|
* if (rc == SSH_AUTH_SUCCESS) return rc;
|
|
|
|
|
* }
|
|
|
|
|
* return SSH_AUTH_ERROR;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @subsection banner
|
|
|
|
|
*
|
|
|
|
|
* The SSH server might send a banner, which you can retrieve with
|
|
|
|
|
* ssh_get_issue_banner(), then display to the user.
|
|
|
|
|
*
|
|
|
|
|
* The following example shows how to retrieve and dispose the issue banner:
|
|
|
|
|
*
|
|
|
|
|
* @code
|
|
|
|
|
* /* Marche pas sans ssh_userauth_none(), du moins en 0.4.2 */
|
|
|
|
|
* int display_banner(ssh_session session)
|
|
|
|
|
* {
|
|
|
|
|
* int rc;
|
|
|
|
|
* char *banner;
|
|
|
|
|
*
|
|
|
|
|
* rc = ssh_userauth_none(session, NULL);
|
|
|
|
|
* if (rc == SSH_AUTH_ERROR)
|
|
|
|
|
* return rc;
|
|
|
|
|
*
|
|
|
|
|
* banner = ssh_get_issue_banner(session);
|
|
|
|
|
* if (banner)
|
|
|
|
|
* {
|
|
|
|
|
* printf("%s\n", banner);
|
|
|
|
|
* free(banner);
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* return rc;
|
|
|
|
|
* }
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @page tbd Chapter 3: To be done
|
|
|
|
|
* @section opening_shell Opening a shell
|
|
|
|
|
*
|
|
|
|
|
* *** To be written ***
|
|
|
|
|
*
|
|
|
|
|
* @section remote_commands Passing remote commands
|
|
|
|
|
*
|
|
|
|
|
* *** To be written ***
|
|
|
|
|
*
|
|
|
|
|
* @section sftp_subsystem The SFTP subsystem
|
|
|
|
|
*
|
|
|
|
|
* *** To be written ***
|
|
|
|
|
*
|
|
|
|
|
* @section scp_subsystem The SCP subsystem
|
|
|
|
|
*
|
|
|
|
|
* *** To be written ***
|
|
|
|
|
*
|
|
|
|
|
* @section threads Working with threads
|
|
|
|
|
*
|
|
|
|
|
* *** To be written ***
|
|
|
|
|
*
|
|
|
|
|
* @section forwarding_connections Forwarding connections
|
|
|
|
|
*
|
|
|
|
|
* *** To be written ***
|
|
|
|
|
*
|
|
|
|
|
*/
|