diff --git a/doc/tutorial.dox b/doc/tutorial.dox index fee1a9c0..862d4531 100644 --- a/doc/tutorial.dox +++ b/doc/tutorial.dox @@ -3,158 +3,286 @@ * * @section introduction Introduction * - * Before inserting SSH hooks into your programs, you must know some basics - * about the SSH protocol, and understand why the SSH library must implement - * them. A lot of the protocol specifications are hidden by the SSH library API - * (of course, that's the benefits of using a library) but some still needs an - * attention from the programmer. + * 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. * - * The SSH protocol was designed for some goals which I state here : + * 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. * - * - Privacy of data - * - Data integrity (alteration of data is always detected) - * - Authentication of the server - * - Authentication of the client + * 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. * - * The following document explains how to set up a client-side connection. libssh - * supports both client and server side of the SSH protocol. If you are going to - * program a server, I suggest you try first to code some client-side programs - * in order to understand how libssh works. - * - * The client MUST be sure who's speaking to before entering into any - * authentication way. That's where the end programmer must ensure the given - * fingerprints *are* from the legitimate server. A SSH connection must follow - * the following steps: + * This tutorial describes libssh version 0.5.0. * - * - Before connecting the socket, 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 made. 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 of the known hosts - * file. - * - * - The client must authenticate : the classical ways are password and - * public keys (from dsa and rsa key-pairs generated by openssh). It is - * harmless to authenticate to a fake server with these keys because the - * protocol ensures the data you sign can't be used twice. It just avoids - * man-in-the-middle attacks. If a SSH-agent is running, it is possible - * to use it. + * Table of contents: * - * - 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 channel. + * @subpage session * - * - With the channel you opened, you can do several things : - * - Open a shell. You may want to request a pseudo virtual terminal before, - * - Execute a single command, - * - Invoke the sftp subsystem. - * - invoke your own subsystem. This is out the scope of this document but it is easy to do. + * @subpage details * - * - When everything is finished, just close the channels, and then the connection. + * @subpage tbd * - * At every place, a function which returns an error code (typically SSH_ERROR for int - * values, NULL for pointers) also sets an error message and an error code. + * + * @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. * * - * @section setup Creating the session and setting options + * @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 must use ssh_new(). Don't forget to always verify that the allocation - * successed. + * 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 - * int main(){ + * #include + * + * int main() + * { * ssh_session my_ssh_session = ssh_new(); - * if(my_ssh_session == NULL) + * 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_free() does this. - * - * The ssh_options_set() is a setter for the options of the session. The most important options are the - * following : - * - SSH_OPTIONS_HOST - * - SSH_OPTIONS_PORT - * - SSH_OPTIONS_USER - * - SSH_OPTIONS_LOG_VERBOSITY - * - ... * - * The complete list 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. + * 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 - * int main(){ + * #include + * + * int main() + * { + * ssh_session my_ssh_session; * int verbosity = SSH_LOG_PROTOCOL; * int port = 22; - * ssh_session my_ssh_session = ssh_new(); - * if(my_ssh_session == NULL) + * + * 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 - * It's no more complicated than that. A few pointers on additionnal functions related to the session - * and options setting : + * + * 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 - * @section 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: + * + * @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 + * #include * #include - * int main(){ + * + * int main() + * { + * ssh_session my_ssh_session; * int ret; - * ssh_session my_ssh_session = ssh_new(); - * if(my_ssh_session == NULL) + * + * 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", + * if (ret != SSH_OK) + * { + * fprintf(stderr, "Error connecting to localhost: %s\n", * ssh_get_error(my_ssh_session)); - * else - * ssh_disconnect(my_ssh_session); + * exit(-1); + * } + * + * ... + * + * ssh_disconnect(my_ssh_session); * ssh_free(my_ssh_session); + * } * @endcode - * - * Once you're connected, the following step is mandatory : you must check that the server + * + * + * @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 check into the known host file (~/.ssh/known_hosts on unix) - * for the server hostname's pattern and check that this host is present or not in the list. * - * The second way is of using ssh_get_pubkey_hash() to get a binary version of the public key hash. - * You can then use your own database to check if this public key is known and secure. You can also - * use that function to show the public key hash to the user, in case he knows what the public - * key hash is (some paranoid people writes their public key hashes on paper before going abroad, - * just in case ...). + * 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. * - * Once you concluded that the host is valid and worth being added in the known hosts file, just use - * ssh_write_knownhost(). The following example is part of the examples suite available in the examples/ - * directory. - * - * @include knownhosts.c + * 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 @@ -164,64 +292,237 @@ * @see ssh_is_server_known * @see ssh_write_knownhost * - * @section auth Authentication * - * The authentication process is the way a service provider can identify a user and verify its - * identity. The authorization process is about authorizing the authenticated user the access - * to ressources. In SSH, the two concepts are linked. After authentication, the server can - * allow the user access to several ressources such as port forwarding, shell, sftp subsystem, - * ... - * libssh supports several authentication ways: - * - "none" method. This method serves to get the available authentications methods, as well as - * giving a chance to the server to authenticate the user with just his login. Some very old - * hardware are using 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 at 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 by the SSH agent as we'll see - * later. - * - * And last but not least, all of this methods can be combined. You can for instance force the user to - * authenticate with at least two of the authentication methods. In that case, we speak 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. + * @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 + * #include + * #include + * + * 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 pubkeys Public keys - * - * The public key authentication is the only method which 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). - * 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 which 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 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 give an other mean of authentication - (like password). - * - * The ssh_userauth_autopubkey function also tries to authenticate using the SSH agent, if you have one - * running, or the "none" method. + * @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 + * + * 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 * - * If you wish to authenticate by 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(). - * * @see ssh_userauth_autopubkey * @see ssh_userauth_offer_pubkey * @see ssh_userauth_pubkey @@ -230,79 +531,185 @@ * @see string_free * @see privatekey_from_file * @see privatekey_free - * - * @subsection password Password - * - * The function ssh_userauth_password() serves the purpose of authenticating using a password. - * It will return SSH_AUTH_SUCCESS if the password worked, 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 plaintext-passwords into the SSH server. - * + * + * + * @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 Keyboard-interactive - * - * The keyboard-interactive method is, as its name tells, interactive. The server will issue one or more - * challenges, which the used has to answer, until the server takes an authentication decision. + * + * + * @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. + * 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 which 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. + * 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 this function (just put user and submethods to NULL) and store the answer. If the answer is - * SSH_AUTH_INFO, it means the server has sent a few questions to ask your user, which you can retrieve 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 back ssh_userauth_kbdint() and start the process again until this functions returns something else - * than SSH_AUTH_INFO. - * - * Few remarks : + * 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. And explanation is given - * in the following documentation. - * A little note about how to use the information from keyboard-interactive authentication, coming from - * The RFC itself (rfc4256) : - * + * - 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 + * + * 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 @@ -310,24 +717,127 @@ * @see ssh_userauth_kbdint_getprompt * @see ssh_userauth_kbdint_setanswer() * - * @subsection none "none" * - * In fact this mode only serve to get the list of supported authentications, or to be authenticated - * **without** any credental. It happens on anonymous services for an example. You can try authentication - * with this mode by using ssh_userauth_none(). - * It also serves to get the banner message from the server, if any. You should always firstly try this method, - * at least for getting the banner, then to enter if there is no password at all. You're advised to use - * ssh_userauth_autopubkey instead. - * if the account has no password (and the server is configured to let you pass), ssh_userauth_none might - * answer SSH_AUTH_SUCCESS. - - * @subsection banner Issue banner - * ssh_get_banner() retrieves the issue banner that a server may send. That banner may be receive right after a - * call to ssh_userauth_none(). - * - * @subsection auth_example example - * The following code is an example of authentication using libssh. It's the content of the examples/authentication.c - * file. + * @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 *** * - * @include authentication.c */