1
1

Tutorial doxyginized and updated up to auth sect.

Этот коммит содержится в:
Aris Adamantiadis 2009-12-20 23:21:59 +01:00
родитель 43c1245396
Коммит 91f7d127ea

Просмотреть файл

@ -3,39 +3,45 @@
* *
* @section introduction Introduction * @section introduction Introduction
* *
* Before inserting ssh hooks into your programs, you must know some basics * Before inserting SSH hooks into your programs, you must know some basics
* about the ssh protocol, and understand why the ssh library must implement * about the SSH protocol, and understand why the SSH library must implement
* them. Lot of the protocols specifications are hidden by the ssh library API * them. A lot of the protocol specifications are hidden by the SSH library API
* (of course !) but some still needs an attention from the end-user * (of course, that's the benefits of using a library) but some still needs an
* programmer. Note that libssh is still an alpha product, and the API may vary * attention from the programmer.
* from one version to another. The only guess I can make is that the API won't
* radically change.
* *
* The SSH protocol was designed for some goals which I resume here : * The SSH protocol was designed for some goals which I state here :
* *
* - Privacy of data * - Privacy of data
* - Security * - Data integrity (alteration of data is always detected)
* - Authentication of the server * - Authentication of the server
* - Authentication of the client * - Authentication of the client
* *
* 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 * 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 * authentication way. That's where the end programmer must ensure the given
* fingerprints *are* from the legitimate server. A ssh connection must follow * fingerprints *are* from the legitimate server. A SSH connection must follow
* the following steps: * the following steps:
* *
* - Before connecting the socket, you can set up if you wish one or other * - Before connecting the socket, you can set up if you wish one or other
* server public key authentication ie. DSA or RSA. You can choose * server public key authentication i.e. DSA or RSA. You can choose
* cryptographic algorithms you trust and compression algorithms if any. * 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 * - 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 * it, a public key from the server is gained. You MUST verify that the public
* key is legitimate. * key is legitimate, using for instance the MD5 fingerprint of the known hosts
* file.
* - The client must authenticate : the two implemented ways are password, and *
* - The client must authenticate : the classical ways are password and
* public keys (from dsa and rsa key-pairs generated by openssh). It is * 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 * 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 * protocol ensures the data you sign can't be used twice. It just avoids
* man-in-the-middle attacks. * man-in-the-middle attacks. 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 * - Now that the user has been authenticated, you must open one or several
* channels. channels are different subways for information into a single ssh * channels. channels are different subways for information into a single ssh
@ -43,26 +49,285 @@
* (stderr). You can theoretically open an infinity of channel. * (stderr). You can theoretically open an infinity of channel.
* *
* - With the channel you opened, you can do several things : * - With the channel you opened, you can do several things :
* - Open a shell. You may want to request a pseudo virtual terminal before * - Open a shell. You may want to request a pseudo virtual terminal before,
* - Execute a command. The virtual terminal is usable, too * - Execute a single command,
* - Invoke the sftp subsystem. (look at chapter 6) * - Invoke the sftp subsystem.
* - invoke your own subsystem. This is out the scope of this document but it is easy to do. * - invoke your own subsystem. This is out the scope of this document but it is easy to do.
* *
* - When everything is finished, just close the channels, and then the connection. * - When everything is finished, just close the channels, and then the connection.
* *
* At every place, a function which returns an error code (typically -1 for int * 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. I * values, NULL for pointers) also sets an error message and an error code.
* high-lined the main steps, now that's you to follow them :) *
* *
* @section setup Creating the session and setting options * @section setup Creating the session and setting options
* *
* TODO * 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.
* @code
* #include <libssh/libssh.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_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.
* Here is a small example of how to use it:
* @code
* #include <libssh/libssh.h>
* int main(){
* int verbosity = SSH_LOG_PROTOCOL;
* int port = 22;
* ssh_session 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_LOG_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 :
* @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 * @section connect Connecting to the server
* *
* TODO * 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 <stdio.h>
* int main(){
* int ret;
* ssh_session 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));
* else
* 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
* 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 ...).
*
* 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
*
* @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
* *
* @section auth Authentication * @section auth Authentication
* *
* TODO * 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 user 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 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 user agent, if you have one
* running, or the "none" method.
*
* 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
* @see publickey_from_file
* @see publickey_from_privatekey
* @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.
*
* @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.
* 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 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.
*
* 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 :
* - 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 informations 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
* @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 "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.
*
* @include authentication.c
*/ */