Blame doc/authentication.dox

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