Blame doc/authentication.dox

Packit 6c0a39
/**
Packit 6c0a39
@page libssh_tutor_authentication Chapter 2: A deeper insight on authentication
Packit 6c0a39
@section authentication_details A deeper insight on authentication
Packit 6c0a39
Packit 6c0a39
In our guided tour, we merely mentioned that the user needed to authenticate.
Packit 6c0a39
We didn't explain much in detail how that was supposed to happen.
Packit 6c0a39
This chapter explains better the four authentication methods: with public keys,
Packit 6c0a39
with a password, with challenges and responses (keyboard-interactive), and with
Packit 6c0a39
no authentication at all.
Packit 6c0a39
Packit 6c0a39
If your software is supposed to connect to an arbitrary server, then you
Packit 6c0a39
might need to support all authentication methods. If your software will
Packit 6c0a39
connect only to a given server, then it might be enough for your software
Packit 6c0a39
to support only the authentication methods used by that server. If you are
Packit 6c0a39
the administrator of the server, it might be your call to choose those
Packit 6c0a39
authentication methods.
Packit 6c0a39
Packit 6c0a39
It is not the purpose of this document to review in detail the advantages
Packit 6c0a39
and drawbacks of each authentication method. You are therefore invited
Packit 6c0a39
to read the abundant documentation on this topic to fully understand the
Packit 6c0a39
advantages and security risks linked to each method.
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection pubkeys Authenticating with public keys
Packit 6c0a39
Packit 6c0a39
libssh is fully compatible with the openssh public and private keys. You
Packit 6c0a39
can either use the automatic public key authentication method provided by
Packit 6c0a39
libssh, or roll your own using the public key functions.
Packit 6c0a39
Packit 6c0a39
The process of authenticating by public key to a server is the following:
Packit 6c0a39
 - you scan a list of files that contain public keys. each key is sent to
Packit 6c0a39
   the SSH server, until the server acknowledges a key (a key it knows can be
Packit 6c0a39
   used to authenticate the user).
Packit 6c0a39
 - then, you retrieve the private key for this key and send a message
Packit 6c0a39
   proving that you know that private key.
Packit 6c0a39
Packit 6c0a39
The function ssh_userauth_autopubkey() does this using the available keys in
Packit 6c0a39
"~/.ssh/".  The return values are the following:
Packit 6c0a39
 - SSH_AUTH_ERROR: some serious error happened during authentication
Packit 6c0a39
 - SSH_AUTH_DENIED: no key matched
Packit 6c0a39
 - SSH_AUTH_SUCCESS: you are now authenticated
Packit 6c0a39
 - SSH_AUTH_PARTIAL: some key matched but you still have to provide an other
Packit 6c0a39
                     mean of authentication (like a password).
Packit 6c0a39
Packit 6c0a39
The ssh_userauth_publickey_auto() function also tries to authenticate using the
Packit 6c0a39
SSH agent, if you have one running, or the "none" method otherwise.
Packit 6c0a39
Packit 6c0a39
If you wish to authenticate with public key by your own, follow these steps:
Packit 6c0a39
 - Retrieve the public key with ssh_pki_import_pubkey_file().
Packit 6c0a39
 - Offer the public key to the SSH server using ssh_userauth_try_publickey().
Packit 6c0a39
   If the return value is SSH_AUTH_SUCCESS, the SSH server accepts to
Packit 6c0a39
   authenticate using the public key and you can go to the next step.
Packit 6c0a39
 - Retrieve the private key, using the ssh_pki_import_privkey_file() function.
Packit 6c0a39
   If a passphrase is needed, either the passphrase specified as argument or
Packit 6c0a39
   a callback will be used.
Packit 6c0a39
 - Authenticate using ssh_userauth_publickey() with your private key.
Packit 6c0a39
 - Do not forget cleaning up memory using ssh_key_free().
Packit 6c0a39
Packit 6c0a39
Here is a minimalistic example of public key authentication:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int authenticate_pubkey(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  rc = ssh_userauth_publickey_auto(session, NULL, NULL);
Packit 6c0a39
Packit 6c0a39
  if (rc == SSH_AUTH_ERROR)
Packit 6c0a39
  {
Packit 6c0a39
     fprintf(stderr, "Authentication failed: %s\n",
Packit 6c0a39
       ssh_get_error(session));
Packit 6c0a39
     return SSH_AUTH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
@see ssh_userauth_publickey_auto()
Packit 6c0a39
@see ssh_userauth_try_publickey()
Packit 6c0a39
@see ssh_userauth_publickey()
Packit 6c0a39
@see ssh_pki_import_pubkey_file()
Packit 6c0a39
@see ssh_pki_import_privkey_file()
Packit 6c0a39
@see ssh_key_free()
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection password Authenticating with a password
Packit 6c0a39
Packit 6c0a39
The function ssh_userauth_password() serves the purpose of authenticating
Packit 6c0a39
using a password. It will return SSH_AUTH_SUCCESS if the password worked,
Packit 6c0a39
or one of other constants otherwise. It's your work to ask the password
Packit 6c0a39
and to deallocate it in a secure manner.
Packit 6c0a39
Packit 6c0a39
If your server complains that the password is wrong, but you can still
Packit 6c0a39
authenticate using openssh's client (issuing password), it's probably
Packit 6c0a39
because openssh only accept keyboard-interactive. Switch to
Packit 6c0a39
keyboard-interactive authentication, or try to configure plain text passwords
Packit 6c0a39
on the SSH server.
Packit 6c0a39
Packit 6c0a39
Here is a small example of password authentication:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int authenticate_password(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  char *password;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  password = getpass("Enter your password: ");
Packit 6c0a39
  rc = ssh_userauth_password(session, NULL, password);
Packit 6c0a39
  if (rc == SSH_AUTH_ERROR)
Packit 6c0a39
  {
Packit 6c0a39
     fprintf(stderr, "Authentication failed: %s\n",
Packit 6c0a39
       ssh_get_error(session));
Packit 6c0a39
     return SSH_AUTH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
@see ssh_userauth_password
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection keyb_int The keyboard-interactive authentication method
Packit 6c0a39
Packit 6c0a39
The keyboard-interactive method is, as its name tells, interactive. The
Packit 6c0a39
server will issue one or more challenges that the user has to answer,
Packit 6c0a39
until the server takes an authentication decision.
Packit 6c0a39
Packit 6c0a39
ssh_userauth_kbdint() is the the main keyboard-interactive function.
Packit 6c0a39
It will return SSH_AUTH_SUCCESS,SSH_AUTH_DENIED, SSH_AUTH_PARTIAL,
Packit 6c0a39
SSH_AUTH_ERROR, or SSH_AUTH_INFO, depending on the result of the request.
Packit 6c0a39
Packit 6c0a39
The keyboard-interactive authentication method of SSH2 is a feature that
Packit 6c0a39
permits the server to ask a certain number of questions in an interactive
Packit 6c0a39
manner to the client, until it decides to accept or deny the login.
Packit 6c0a39
Packit 6c0a39
To begin, you call ssh_userauth_kbdint() (just set user and submethods to
Packit 6c0a39
NULL) and store the answer.
Packit 6c0a39
Packit 6c0a39
If the answer is SSH_AUTH_INFO, it means that the server has sent a few
Packit 6c0a39
questions that you should ask the user. You can retrieve these questions
Packit 6c0a39
with the following functions: ssh_userauth_kbdint_getnprompts(),
Packit 6c0a39
ssh_userauth_kbdint_getname(), ssh_userauth_kbdint_getinstruction(), and
Packit 6c0a39
ssh_userauth_kbdint_getprompt().
Packit 6c0a39
Packit 6c0a39
Set the answer for each question in the challenge using
Packit 6c0a39
ssh_userauth_kbdint_setanswer().
Packit 6c0a39
Packit 6c0a39
Then, call again ssh_userauth_kbdint() and start the process again until
Packit 6c0a39
these functions returns something else than SSH_AUTH_INFO.
Packit 6c0a39
Packit 6c0a39
Here are a few remarks:
Packit 6c0a39
 - Even the first call can return SSH_AUTH_DENIED or SSH_AUTH_SUCCESS.
Packit 6c0a39
 - The server can send an empty question set (this is the default behavior
Packit 6c0a39
   on my system) after you have sent the answers to the first questions.
Packit 6c0a39
   You must still parse the answer, it might contain some
Packit 6c0a39
   message from the server saying hello or such things. Just call
Packit 6c0a39
   ssh_userauth_kbdint() until needed.
Packit 6c0a39
 - The meaning of "name", "prompt", "instruction" may be a little
Packit 6c0a39
   confusing. An explanation is given in the RFC section that follows.
Packit 6c0a39
Packit 6c0a39
Here is a little note about how to use the information from
Packit 6c0a39
keyboard-interactive authentication, coming from the RFC itself (rfc4256):
Packit 6c0a39
Packit 6c0a39
@verbatim
Packit 6c0a39
Packit 6c0a39
  3.3 User Interface Upon receiving a request message, the client SHOULD
Packit 6c0a39
  prompt the user as follows: A command line interface (CLI) client SHOULD
Packit 6c0a39
  print the name and instruction (if non-empty), adding newlines. Then for
Packit 6c0a39
  each prompt in turn, the client SHOULD display the prompt and read the
Packit 6c0a39
  user input.
Packit 6c0a39
Packit 6c0a39
  A graphical user interface (GUI) client has many choices on how to prompt
Packit 6c0a39
  the user. One possibility is to use the name field (possibly prefixed
Packit 6c0a39
  with the application's name) as the title of a dialog window in which
Packit 6c0a39
  the prompt(s) are presented. In that dialog window, the instruction field
Packit 6c0a39
  would be a text message, and the prompts would be labels for text entry
Packit 6c0a39
  fields. All fields SHOULD be presented to the user, for example an
Packit 6c0a39
  implementation SHOULD NOT discard the name field because its windows lack
Packit 6c0a39
  titles; it SHOULD instead find another way to display this information. If
Packit 6c0a39
  prompts are presented in a dialog window, then the client SHOULD NOT
Packit 6c0a39
  present each prompt in a separate window.
Packit 6c0a39
Packit 6c0a39
  All clients MUST properly handle an instruction field with embedded
Packit 6c0a39
  newlines. They SHOULD also be able to display at least 30 characters for
Packit 6c0a39
  the name and prompts. If the server presents names or prompts longer than 30
Packit 6c0a39
  characters, the client MAY truncate these fields to the length it can
Packit 6c0a39
  display. If the client does truncate any fields, there MUST be an obvious
Packit 6c0a39
  indication that such truncation has occurred.
Packit 6c0a39
Packit 6c0a39
  The instruction field SHOULD NOT be truncated. Clients SHOULD use control
Packit 6c0a39
  character filtering as discussed in [SSH-ARCH] to avoid attacks by
Packit 6c0a39
  including terminal control characters in the fields to be displayed.
Packit 6c0a39
Packit 6c0a39
  For each prompt, the corresponding echo field indicates whether or not
Packit 6c0a39
  the user input should be echoed as characters are typed. Clients SHOULD
Packit 6c0a39
  correctly echo/mask user input for each prompt independently of other
Packit 6c0a39
  prompts in the request message. If a client does not honor the echo field
Packit 6c0a39
  for whatever reason, then the client MUST err on the side of
Packit 6c0a39
  masking input. A GUI client might like to have a checkbox toggling
Packit 6c0a39
  echo/mask. Clients SHOULD NOT add any additional characters to the prompt
Packit 6c0a39
  such as ": " (colon-space); the server is responsible for supplying all
Packit 6c0a39
  text to be displayed to the user. Clients MUST also accept empty responses
Packit 6c0a39
  from the user and pass them on as empty strings.
Packit 6c0a39
@endverbatim
Packit 6c0a39
Packit 6c0a39
The following example shows how to perform keyboard-interactive authentication:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int authenticate_kbdint(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  rc = ssh_userauth_kbdint(session, NULL, NULL);
Packit 6c0a39
  while (rc == SSH_AUTH_INFO)
Packit 6c0a39
  {
Packit 6c0a39
    const char *name, *instruction;
Packit 6c0a39
    int nprompts, iprompt;
Packit 6c0a39
Packit 6c0a39
    name = ssh_userauth_kbdint_getname(session);
Packit 6c0a39
    instruction = ssh_userauth_kbdint_getinstruction(session);
Packit 6c0a39
    nprompts = ssh_userauth_kbdint_getnprompts(session);
Packit 6c0a39
Packit 6c0a39
    if (strlen(name) > 0)
Packit 6c0a39
      printf("%s\n", name);
Packit 6c0a39
    if (strlen(instruction) > 0)
Packit 6c0a39
      printf("%s\n", instruction);
Packit 6c0a39
    for (iprompt = 0; iprompt < nprompts; iprompt++)
Packit 6c0a39
    {
Packit 6c0a39
      const char *prompt;
Packit 6c0a39
      char echo;
Packit 6c0a39
Packit 6c0a39
      prompt = ssh_userauth_kbdint_getprompt(session, iprompt, &echo);
Packit 6c0a39
      if (echo)
Packit 6c0a39
      {
Packit 6c0a39
        char buffer[128], *ptr;
Packit 6c0a39
Packit 6c0a39
        printf("%s", prompt);
Packit 6c0a39
        if (fgets(buffer, sizeof(buffer), stdin) == NULL)
Packit 6c0a39
          return SSH_AUTH_ERROR;
Packit 6c0a39
        buffer[sizeof(buffer) - 1] = '\0';
Packit 6c0a39
        if ((ptr = strchr(buffer, '\n')) != NULL)
Packit 6c0a39
          *ptr = '\0';
Packit 6c0a39
        if (ssh_userauth_kbdint_setanswer(session, iprompt, buffer) < 0)
Packit 6c0a39
          return SSH_AUTH_ERROR;
Packit 6c0a39
        memset(buffer, 0, strlen(buffer));
Packit 6c0a39
      }
Packit 6c0a39
      else
Packit 6c0a39
      {
Packit 6c0a39
        char *ptr;
Packit 6c0a39
Packit 6c0a39
        ptr = getpass(prompt);
Packit 6c0a39
        if (ssh_userauth_kbdint_setanswer(session, iprompt, ptr) < 0)
Packit 6c0a39
          return SSH_AUTH_ERROR;
Packit 6c0a39
      }
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_userauth_kbdint(session, NULL, NULL);
Packit 6c0a39
  }
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
@see ssh_userauth_kbdint()
Packit 6c0a39
@see ssh_userauth_kbdint_getnprompts()
Packit 6c0a39
@see ssh_userauth_kbdint_getname()
Packit 6c0a39
@see ssh_userauth_kbdint_getinstruction()
Packit 6c0a39
@see ssh_userauth_kbdint_getprompt()
Packit 6c0a39
@see ssh_userauth_kbdint_setanswer()
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection none Authenticating with "none" method
Packit 6c0a39
Packit 6c0a39
The primary purpose of the "none" method is to get authenticated **without**
Packit 6c0a39
any credential. Don't do that, use one of the other authentication methods,
Packit 6c0a39
unless you really want to grant anonymous access.
Packit 6c0a39
Packit 6c0a39
If the account has no password, and if the server is configured to let you
Packit 6c0a39
pass, ssh_userauth_none() might answer SSH_AUTH_SUCCESS.
Packit 6c0a39
Packit 6c0a39
The following example shows how to perform "none" authentication:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int authenticate_none(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  rc = ssh_userauth_none(session, NULL);
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
@subsection auth_list Getting the list of supported authentications
Packit 6c0a39
Packit 6c0a39
You are not meant to choose a given authentication method, you can
Packit 6c0a39
let the server tell you which methods are available. Once you know them,
Packit 6c0a39
you try them one after the other.
Packit 6c0a39
Packit 6c0a39
The following example shows how to get the list of available authentication
Packit 6c0a39
methods with ssh_userauth_list() and how to use the result:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int test_several_auth_methods(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  int method, rc;
Packit 6c0a39
Packit 6c0a39
  rc = ssh_userauth_none(session, NULL);
Packit 6c0a39
  if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_ERROR) {
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  method = ssh_userauth_list(session, NULL);
Packit 6c0a39
Packit 6c0a39
  if (method & SSH_AUTH_METHOD_NONE)
Packit 6c0a39
  { // For the source code of function authenticate_none(),
Packit 6c0a39
    // refer to the corresponding example
Packit 6c0a39
    rc = authenticate_none(session);
Packit 6c0a39
    if (rc == SSH_AUTH_SUCCESS) return rc;
Packit 6c0a39
  }
Packit 6c0a39
  if (method & SSH_AUTH_METHOD_PUBLICKEY)
Packit 6c0a39
  { // For the source code of function authenticate_pubkey(),
Packit 6c0a39
    // refer to the corresponding example
Packit 6c0a39
    rc = authenticate_pubkey(session);
Packit 6c0a39
    if (rc == SSH_AUTH_SUCCESS) return rc;
Packit 6c0a39
  }
Packit 6c0a39
  if (method & SSH_AUTH_METHOD_INTERACTIVE)
Packit 6c0a39
  { // For the source code of function authenticate_kbdint(),
Packit 6c0a39
    // refer to the corresponding example
Packit 6c0a39
    rc = authenticate_kbdint(session);
Packit 6c0a39
    if (rc == SSH_AUTH_SUCCESS) return rc;
Packit 6c0a39
  }
Packit 6c0a39
  if (method & SSH_AUTH_METHOD_PASSWORD)
Packit 6c0a39
  { // For the source code of function authenticate_password(),
Packit 6c0a39
    // refer to the corresponding example
Packit 6c0a39
    rc = authenticate_password(session);
Packit 6c0a39
    if (rc == SSH_AUTH_SUCCESS) return rc;
Packit 6c0a39
  }
Packit 6c0a39
  return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection banner Getting the banner
Packit 6c0a39
Packit 6c0a39
The SSH server might send a banner, which you can retrieve with
Packit 6c0a39
ssh_get_issue_banner(), then display to the user.
Packit 6c0a39
Packit 6c0a39
The following example shows how to retrieve and dispose the issue banner:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int display_banner(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  int rc;
Packit 6c0a39
  char *banner;
Packit 6c0a39
Packit 6c0a39
/*
Packit 6c0a39
 *** Does not work without calling ssh_userauth_none() first ***
Packit 6c0a39
 *** That will be fixed ***
Packit 6c0a39
*/
Packit 6c0a39
  rc = ssh_userauth_none(session, NULL);
Packit 6c0a39
  if (rc == SSH_AUTH_ERROR)
Packit 6c0a39
    return rc;
Packit 6c0a39
Packit 6c0a39
  banner = ssh_get_issue_banner(session);
Packit 6c0a39
  if (banner)
Packit 6c0a39
  {
Packit 6c0a39
    printf("%s\n", banner);
Packit 6c0a39
    free(banner);
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return rc;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
*/