Blame src/auth.c

Packit 6c0a39
/*
Packit 6c0a39
 * auth.c - Authentication with SSH protocols
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2003-2013 by Aris Adamantiadis <aris@0xbadc0de.be>
Packit 6c0a39
 * Copyright (c) 2008-2013 Andreas Schneider <asn@cryptomilk.org>
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
#include <netinet/in.h>
Packit 6c0a39
#include <arpa/inet.h>
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/crypto.h"
Packit 6c0a39
#include "libssh/ssh2.h"
Packit 6c0a39
#include "libssh/buffer.h"
Packit 6c0a39
#include "libssh/agent.h"
Packit 6c0a39
#include "libssh/misc.h"
Packit 6c0a39
#include "libssh/packet.h"
Packit 6c0a39
#include "libssh/session.h"
Packit 6c0a39
#include "libssh/keys.h"
Packit 6c0a39
#include "libssh/auth.h"
Packit 6c0a39
#include "libssh/pki.h"
Packit 6c0a39
#include "libssh/gssapi.h"
Packit 6c0a39
#include "libssh/legacy.h"
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @defgroup libssh_auth The SSH authentication functions.
Packit 6c0a39
 * @ingroup libssh
Packit 6c0a39
 *
Packit 6c0a39
 * Functions to authenticate with a server.
Packit 6c0a39
 *
Packit 6c0a39
 * @{
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Ask access to the ssh-userauth service.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session   The SSH session handle.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_OK on success, SSH_ERROR on error.
Packit 6c0a39
 * @returns SSH_AGAIN on nonblocking mode, if calling that function
Packit 6c0a39
 * again is necessary
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_userauth_request_service(ssh_session session) {
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_service_request(session, "ssh-userauth");
Packit 6c0a39
    if ((rc != SSH_OK) && (rc != SSH_AGAIN)) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                "Failed to request \"ssh-userauth\" service");
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int ssh_auth_response_termination(void *user) {
Packit 6c0a39
    ssh_session session = (ssh_session)user;
Packit 6c0a39
    switch (session->auth.state) {
Packit 6c0a39
        case SSH_AUTH_STATE_NONE:
Packit 6c0a39
        case SSH_AUTH_STATE_KBDINT_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_GSSAPI_TOKEN:
Packit 6c0a39
        case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_AUTH_NONE_SENT:
Packit 6c0a39
            return 0;
Packit 6c0a39
        default:
Packit 6c0a39
            return 1;
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static const char *ssh_auth_get_current_method(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    const char *method = "unknown";
Packit 6c0a39
Packit 6c0a39
    switch (session->auth.current_method) {
Packit 6c0a39
    case SSH_AUTH_METHOD_NONE:
Packit 6c0a39
        method = "none";
Packit 6c0a39
        break;
Packit 6c0a39
    case SSH_AUTH_METHOD_PASSWORD:
Packit 6c0a39
        method = "password";
Packit 6c0a39
        break;
Packit 6c0a39
    case SSH_AUTH_METHOD_PUBLICKEY:
Packit 6c0a39
        method = "publickey";
Packit 6c0a39
        break;
Packit 6c0a39
    case SSH_AUTH_METHOD_HOSTBASED:
Packit 6c0a39
        method = "hostbased";
Packit 6c0a39
        break;
Packit 6c0a39
    case SSH_AUTH_METHOD_INTERACTIVE:
Packit 6c0a39
        method = "keyboard interactive";
Packit 6c0a39
        break;
Packit 6c0a39
    case SSH_AUTH_METHOD_GSSAPI_MIC:
Packit 6c0a39
        method = "gssapi";
Packit 6c0a39
        break;
Packit 6c0a39
    default:
Packit 6c0a39
        break;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return method;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Wait for a response of an authentication function.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session   The SSH session.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_AUTH_SUCCESS Authentication success, or pubkey accepted
Packit 6c0a39
 *          SSH_AUTH_PARTIAL Authentication succeeded but another mean
Packit 6c0a39
 *                           of authentication is needed.
Packit 6c0a39
 *          SSH_AUTH_INFO    Data for keyboard-interactive
Packit 6c0a39
 *          SSH_AUTH_AGAIN   In nonblocking mode, call has to be made again
Packit 6c0a39
 *          SSH_AUTH_ERROR   Error during the process.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_userauth_get_response(ssh_session session) {
Packit 6c0a39
    int rc = SSH_AUTH_ERROR;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
Packit 6c0a39
        ssh_auth_response_termination, session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    if (!ssh_auth_response_termination(session)) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    switch(session->auth.state) {
Packit 6c0a39
        case SSH_AUTH_STATE_ERROR:
Packit 6c0a39
            rc = SSH_AUTH_ERROR;
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_AUTH_STATE_FAILED:
Packit 6c0a39
            rc = SSH_AUTH_DENIED;
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_AUTH_STATE_INFO:
Packit 6c0a39
            rc = SSH_AUTH_INFO;
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_AUTH_STATE_PARTIAL:
Packit 6c0a39
            rc = SSH_AUTH_PARTIAL;
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_AUTH_STATE_PK_OK:
Packit 6c0a39
        case SSH_AUTH_STATE_SUCCESS:
Packit 6c0a39
            rc = SSH_AUTH_SUCCESS;
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_AUTH_STATE_KBDINT_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_GSSAPI_TOKEN:
Packit 6c0a39
        case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_AUTH_NONE_SENT:
Packit 6c0a39
        case SSH_AUTH_STATE_NONE:
Packit 6c0a39
            /* not reached */
Packit 6c0a39
            rc = SSH_AUTH_ERROR;
Packit 6c0a39
            break;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handles a SSH_USERAUTH_BANNER packet.
Packit 6c0a39
 *
Packit 6c0a39
 * This banner should be shown to user prior to authentication
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_userauth_banner) {
Packit 6c0a39
    ssh_string banner;
Packit 6c0a39
    (void)type;
Packit 6c0a39
    (void)user;
Packit 6c0a39
Packit 6c0a39
    banner = ssh_buffer_get_ssh_string(packet);
Packit 6c0a39
    if (banner == NULL) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                "Invalid SSH_USERAUTH_BANNER packet");
Packit 6c0a39
    } else {
Packit 6c0a39
        SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                "Received SSH_USERAUTH_BANNER packet");
Packit 6c0a39
        if (session->banner != NULL)
Packit 6c0a39
            ssh_string_free(session->banner);
Packit 6c0a39
        session->banner = banner;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handles a SSH_USERAUTH_FAILURE packet.
Packit 6c0a39
 *
Packit 6c0a39
 * This handles the complete or partial authentication failure.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
Packit 6c0a39
    const char *current_method = ssh_auth_get_current_method(session);
Packit 6c0a39
    char *auth_methods = NULL;
Packit 6c0a39
    uint8_t partial = 0;
Packit 6c0a39
    int rc;
Packit 6c0a39
    (void) type;
Packit 6c0a39
    (void) user;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_unpack(packet, "sb", &auth_methods, &partial);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                      "Invalid SSH_MSG_USERAUTH_FAILURE message");
Packit 6c0a39
        session->auth.state = SSH_AUTH_STATE_ERROR;
Packit 6c0a39
        goto end;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (partial) {
Packit 6c0a39
        session->auth.state = SSH_AUTH_STATE_PARTIAL;
Packit 6c0a39
        SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
                "Partial success for '%s'. Authentication that can continue: %s",
Packit 6c0a39
                current_method,
Packit 6c0a39
                auth_methods);
Packit 6c0a39
    } else {
Packit 6c0a39
        session->auth.state = SSH_AUTH_STATE_FAILED;
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "Access denied for '%s'. Authentication that can continue: %s",
Packit 6c0a39
                      current_method,
Packit 6c0a39
                      auth_methods);
Packit 6c0a39
        SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
                "%s",
Packit 6c0a39
                ssh_get_error(session));
Packit 6c0a39
Packit 6c0a39
    }
Packit 6c0a39
    session->auth.supported_methods = 0;
Packit 6c0a39
    if (strstr(auth_methods, "password") != NULL) {
Packit 6c0a39
        session->auth.supported_methods |= SSH_AUTH_METHOD_PASSWORD;
Packit 6c0a39
    }
Packit 6c0a39
    if (strstr(auth_methods, "keyboard-interactive") != NULL) {
Packit 6c0a39
        session->auth.supported_methods |= SSH_AUTH_METHOD_INTERACTIVE;
Packit 6c0a39
    }
Packit 6c0a39
    if (strstr(auth_methods, "publickey") != NULL) {
Packit 6c0a39
        session->auth.supported_methods |= SSH_AUTH_METHOD_PUBLICKEY;
Packit 6c0a39
    }
Packit 6c0a39
    if (strstr(auth_methods, "hostbased") != NULL) {
Packit 6c0a39
        session->auth.supported_methods |= SSH_AUTH_METHOD_HOSTBASED;
Packit 6c0a39
    }
Packit 6c0a39
    if (strstr(auth_methods, "gssapi-with-mic") != NULL) {
Packit 6c0a39
        session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_MIC;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
end:
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN;
Packit 6c0a39
    SAFE_FREE(auth_methods);
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handles a SSH_USERAUTH_SUCCESS packet.
Packit 6c0a39
 *
Packit 6c0a39
 * It is also used to communicate the new to the upper levels.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_userauth_success)
Packit 6c0a39
{
Packit 6c0a39
  struct ssh_crypto_struct *crypto = NULL;
Packit 6c0a39
Packit 6c0a39
  (void)packet;
Packit 6c0a39
  (void)type;
Packit 6c0a39
  (void)user;
Packit 6c0a39
Packit 6c0a39
  SSH_LOG(SSH_LOG_DEBUG, "Authentication successful");
Packit 6c0a39
  SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS");
Packit 6c0a39
Packit 6c0a39
  session->auth.state = SSH_AUTH_STATE_SUCCESS;
Packit 6c0a39
  session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
Packit 6c0a39
  session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;
Packit 6c0a39
Packit 6c0a39
  crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
Packit 6c0a39
  if (crypto != NULL && crypto->delayed_compress_out) {
Packit 6c0a39
      SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT");
Packit 6c0a39
      crypto->do_compress_out = 1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
Packit 6c0a39
  if (crypto != NULL && crypto->delayed_compress_in) {
Packit 6c0a39
      SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN");
Packit 6c0a39
      crypto->do_compress_in = 1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
    /* Reset errors by previous authentication methods. */
Packit 6c0a39
    ssh_reset_error(session);
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN;
Packit 6c0a39
  return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Handles a SSH_USERAUTH_PK_OK or SSH_USERAUTH_INFO_REQUEST packet.
Packit 6c0a39
 *
Packit 6c0a39
 * Since the two types of packets share the same code, additional work is done
Packit 6c0a39
 * to understand if we are in a public key or keyboard-interactive context.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok) {
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_TRACE,
Packit 6c0a39
            "Received SSH_USERAUTH_PK_OK/INFO_REQUEST/GSSAPI_RESPONSE");
Packit 6c0a39
Packit 6c0a39
    if (session->auth.state == SSH_AUTH_STATE_KBDINT_SENT) {
Packit 6c0a39
        /* Assuming we are in keyboard-interactive context */
Packit 6c0a39
        SSH_LOG(SSH_LOG_TRACE,
Packit 6c0a39
                "keyboard-interactive context, "
Packit 6c0a39
                "assuming SSH_USERAUTH_INFO_REQUEST");
Packit 6c0a39
        rc = ssh_packet_userauth_info_request(session,type,packet,user);
Packit 6c0a39
#ifdef WITH_GSSAPI
Packit 6c0a39
    } else if (session->auth.state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT) {
Packit 6c0a39
        rc = ssh_packet_userauth_gssapi_response(session, type, packet, user);
Packit 6c0a39
#endif
Packit 6c0a39
    } else if (session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT) {
Packit 6c0a39
        session->auth.state = SSH_AUTH_STATE_PK_OK;
Packit 6c0a39
        SSH_LOG(SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK");
Packit 6c0a39
        rc = SSH_PACKET_USED;
Packit 6c0a39
    } else {
Packit 6c0a39
        session->auth.state = SSH_AUTH_STATE_ERROR;
Packit 6c0a39
        SSH_LOG(SSH_LOG_TRACE, "SSH_USERAUTH_PK_OK received in wrong state");
Packit 6c0a39
        rc = SSH_PACKET_USED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get available authentication methods from the server.
Packit 6c0a39
 *
Packit 6c0a39
 * This requires the function ssh_userauth_none() to be called before the
Packit 6c0a39
 * methods are available. The server MAY return a list of methods that may
Packit 6c0a39
 * continue.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session   The SSH session.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] username  Deprecated, set to NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns             A bitfield of the fllowing values:
Packit 6c0a39
 *                      - SSH_AUTH_METHOD_PASSWORD
Packit 6c0a39
 *                      - SSH_AUTH_METHOD_PUBLICKEY
Packit 6c0a39
 *                      - SSH_AUTH_METHOD_HOSTBASED
Packit 6c0a39
 *                      - SSH_AUTH_METHOD_INTERACTIVE
Packit 6c0a39
 *
Packit 6c0a39
 * @warning Other reserved flags may appear in future versions.
Packit 6c0a39
 * @see ssh_userauth_none()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_list(ssh_session session, const char *username)
Packit 6c0a39
{
Packit 6c0a39
    (void) username; /* unused */
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return session->auth.supported_methods;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Try to authenticate through the "none" method.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session   The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] username    The username, this SHOULD be NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_AUTH_ERROR:   A serious error happened.\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  Authentication failed: use another method\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: Authentication success\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
Packit 6c0a39
 * @note Most server implementations do not permit changing the username during
Packit 6c0a39
 * authentication. The username should only be set with ssh_options_set() only
Packit 6c0a39
 * before you connect to the server.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_none(ssh_session session, const char *username) {
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    switch(session->pending_call_state) {
Packit 6c0a39
        case SSH_PENDING_CALL_NONE:
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_PENDING_CALL_AUTH_NONE:
Packit 6c0a39
            goto pending;
Packit 6c0a39
        default:
Packit 6c0a39
            ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                          "Wrong state (%d) during pending SSH call",
Packit 6c0a39
                          session->pending_call_state);
Packit 6c0a39
            return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    } else if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* request */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bsss",
Packit 6c0a39
            SSH2_MSG_USERAUTH_REQUEST,
Packit 6c0a39
            username ? username : session->opts.username,
Packit 6c0a39
            "ssh-connection",
Packit 6c0a39
            "none"
Packit 6c0a39
            );
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_NONE;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_AUTH_NONE_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_NONE;
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN) {
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Try to authenticate with the given public key.
Packit 6c0a39
 *
Packit 6c0a39
 * To avoid unnecessary processing and user interaction, the following method
Packit 6c0a39
 * is provided for querying whether authentication using the 'pubkey' would
Packit 6c0a39
 * be possible.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session     The SSH session.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] username    The username, this SHOULD be NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] pubkey      The public key to try.
Packit 6c0a39
 *
Packit 6c0a39
 * @return  SSH_AUTH_ERROR:   A serious error happened.\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  The server doesn't accept that public key as an
Packit 6c0a39
 *                            authentication token. Try another key or another
Packit 6c0a39
 *                            method.\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method.\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: The public key is accepted, you want now to use
Packit 6c0a39
 *                            ssh_userauth_publickey().\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
Packit 6c0a39
 * @note Most server implementations do not permit changing the username during
Packit 6c0a39
 * authentication. The username should only be set with ssh_options_set() only
Packit 6c0a39
 * before you connect to the server.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_try_publickey(ssh_session session,
Packit 6c0a39
                               const char *username,
Packit 6c0a39
                               const ssh_key pubkey)
Packit 6c0a39
{
Packit 6c0a39
    ssh_string pubkey_s = NULL;
Packit 6c0a39
    const char *sig_type_c = NULL;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (pubkey == NULL || !ssh_key_is_public(pubkey)) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid pubkey");
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    switch(session->pending_call_state) {
Packit 6c0a39
        case SSH_PENDING_CALL_NONE:
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY:
Packit 6c0a39
            goto pending;
Packit 6c0a39
        default:
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_FATAL,
Packit 6c0a39
                          "Wrong state (%d) during pending SSH call",
Packit 6c0a39
                          session->pending_call_state);
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Check if the given public key algorithm is allowed */
Packit 6c0a39
    sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type);
Packit 6c0a39
    if (sig_type_c == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "Invalid key type (unknown)");
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "The key algorithm '%s' is not allowed to be used by"
Packit 6c0a39
                      " PUBLICKEY_ACCEPTED_TYPES configuration option",
Packit 6c0a39
                      sig_type_c);
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    } else if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* public key */
Packit 6c0a39
    rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_s);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* request */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
Packit 6c0a39
            SSH2_MSG_USERAUTH_REQUEST,
Packit 6c0a39
            username ? username : session->opts.username,
Packit 6c0a39
            "ssh-connection",
Packit 6c0a39
            "publickey",
Packit 6c0a39
            0, /* private key ? */
Packit 6c0a39
            sig_type_c, /* algo */
Packit 6c0a39
            pubkey_s /* public key */
Packit 6c0a39
            );
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    ssh_string_free(pubkey_s);
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_PUBKEY_OFFER_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY;
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN) {
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_string_free(pubkey_s);
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Authenticate with public/private key or certificate.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session     The SSH session.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] username    The username, this SHOULD be NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] privkey     The private key for authentication.
Packit 6c0a39
 *
Packit 6c0a39
 * @return  SSH_AUTH_ERROR:   A serious error happened.\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  The server doesn't accept that public key as an
Packit 6c0a39
 *                            authentication token. Try another key or another
Packit 6c0a39
 *                            method.\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method.\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: The public key is accepted.\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
Packit 6c0a39
 * @note Most server implementations do not permit changing the username during
Packit 6c0a39
 * authentication. The username should only be set with ssh_options_set() only
Packit 6c0a39
 * before you connect to the server.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_publickey(ssh_session session,
Packit 6c0a39
                           const char *username,
Packit 6c0a39
                           const ssh_key privkey)
Packit 6c0a39
{
Packit 6c0a39
    ssh_string str = NULL;
Packit 6c0a39
    int rc;
Packit 6c0a39
    const char *sig_type_c = NULL;
Packit 6c0a39
    enum ssh_keytypes_e key_type;
Packit 6c0a39
    enum ssh_digest_e hash_type;
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (privkey == NULL || !ssh_key_is_private(privkey)) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid private key");
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    switch(session->pending_call_state) {
Packit 6c0a39
        case SSH_PENDING_CALL_NONE:
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_PENDING_CALL_AUTH_PUBKEY:
Packit 6c0a39
            goto pending;
Packit 6c0a39
        default:
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_FATAL,
Packit 6c0a39
                          "Bad call during pending SSH call in ssh_userauth_try_publickey");
Packit 6c0a39
            return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Cert auth requires presenting the cert type name (*-cert@openssh.com) */
Packit 6c0a39
    key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
Packit 6c0a39
Packit 6c0a39
    /* Check if the given public key algorithm is allowed */
Packit 6c0a39
    sig_type_c = ssh_key_get_signature_algorithm(session, key_type);
Packit 6c0a39
    if (sig_type_c == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "Invalid key type (unknown)");
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "The key algorithm '%s' is not allowed to be used by"
Packit 6c0a39
                      " PUBLICKEY_ACCEPTED_TYPES configuration option",
Packit 6c0a39
                      sig_type_c);
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    } else if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* get public key or cert */
Packit 6c0a39
    rc = ssh_pki_export_pubkey_blob(privkey, &str);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* request */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
Packit 6c0a39
            SSH2_MSG_USERAUTH_REQUEST,
Packit 6c0a39
            username ? username : session->opts.username,
Packit 6c0a39
            "ssh-connection",
Packit 6c0a39
            "publickey",
Packit 6c0a39
            1, /* private key */
Packit 6c0a39
            sig_type_c, /* algo */
Packit 6c0a39
            str /* public key or cert */
Packit 6c0a39
            );
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
    ssh_string_free(str);
Packit 6c0a39
Packit 6c0a39
    /* Get the hash type to be used in the signature based on the key type */
Packit 6c0a39
    hash_type = ssh_key_type_to_hash(session, privkey->type);
Packit 6c0a39
Packit 6c0a39
    /* sign the buffer with the private key */
Packit 6c0a39
    str = ssh_pki_do_sign(session, session->out_buffer, privkey, hash_type);
Packit 6c0a39
    if (str == NULL) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_add_ssh_string(session->out_buffer, str);
Packit 6c0a39
    ssh_string_free(str);
Packit 6c0a39
    str = NULL;
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_PUBKEY_AUTH_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY;
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN) {
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_string_free(str);
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
static int ssh_userauth_agent_publickey(ssh_session session,
Packit 6c0a39
                                        const char *username,
Packit 6c0a39
                                        ssh_key pubkey)
Packit 6c0a39
{
Packit 6c0a39
    ssh_string pubkey_s = NULL;
Packit 6c0a39
    ssh_string sig_blob = NULL;
Packit 6c0a39
    const char *sig_type_c = NULL;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    switch(session->pending_call_state) {
Packit 6c0a39
        case SSH_PENDING_CALL_NONE:
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_PENDING_CALL_AUTH_AGENT:
Packit 6c0a39
            goto pending;
Packit 6c0a39
        default:
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_FATAL,
Packit 6c0a39
                          "Bad call during pending SSH call in ssh_userauth_try_publickey");
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    } else if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* public key */
Packit 6c0a39
    rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_s);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Check if the given public key algorithm is allowed */
Packit 6c0a39
    sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type);
Packit 6c0a39
    if (sig_type_c == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "Invalid key type (unknown)");
Packit 6c0a39
        SSH_STRING_FREE(pubkey_s);
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
Packit 6c0a39
        ssh_set_error(session, SSH_REQUEST_DENIED,
Packit 6c0a39
                      "The key algorithm '%s' is not allowed to be used by"
Packit 6c0a39
                      " PUBLICKEY_ACCEPTED_TYPES configuration option",
Packit 6c0a39
                      sig_type_c);
Packit 6c0a39
        SSH_STRING_FREE(pubkey_s);
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* request */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
Packit 6c0a39
            SSH2_MSG_USERAUTH_REQUEST,
Packit 6c0a39
            username ? username : session->opts.username,
Packit 6c0a39
            "ssh-connection",
Packit 6c0a39
            "publickey",
Packit 6c0a39
            1, /* private key */
Packit 6c0a39
            sig_type_c, /* algo */
Packit 6c0a39
            pubkey_s /* public key */
Packit 6c0a39
            );
Packit 6c0a39
    SSH_STRING_FREE(pubkey_s);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* sign the buffer with the private key */
Packit 6c0a39
    sig_blob = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey);
Packit 6c0a39
    if (sig_blob == NULL) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_add_ssh_string(session->out_buffer, sig_blob);
Packit 6c0a39
    SSH_STRING_FREE(sig_blob);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_PUBKEY_AUTH_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_AGENT;
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN) {
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
    SSH_STRING_FREE(pubkey_s);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
enum ssh_agent_state_e {
Packit 6c0a39
    SSH_AGENT_STATE_NONE = 0,
Packit 6c0a39
    SSH_AGENT_STATE_PUBKEY,
Packit 6c0a39
    SSH_AGENT_STATE_AUTH
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
struct ssh_agent_state_struct {
Packit 6c0a39
    enum ssh_agent_state_e state;
Packit 6c0a39
    ssh_key pubkey;
Packit 6c0a39
    char *comment;
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
/* Internal function */
Packit 6c0a39
void ssh_agent_state_free(void *data) {
Packit 6c0a39
    struct ssh_agent_state_struct *state = data;
Packit 6c0a39
Packit 6c0a39
    if (state) {
Packit 6c0a39
        ssh_string_free_char(state->comment);
Packit 6c0a39
        ssh_key_free(state->pubkey);
Packit 6c0a39
        free (state);
Packit 6c0a39
    }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Try to do public key authentication with ssh agent.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  username The username, this SHOULD be NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @return  SSH_AUTH_ERROR:   A serious error happened.\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  The server doesn't accept that public key as an
Packit 6c0a39
 *                            authentication token. Try another key or another
Packit 6c0a39
 *                            method.\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method.\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: The public key is accepted, you want now to use
Packit 6c0a39
 *                            ssh_userauth_publickey().\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
Packit 6c0a39
 * @note Most server implementations do not permit changing the username during
Packit 6c0a39
 * authentication. The username should only be set with ssh_options_set() only
Packit 6c0a39
 * before you connect to the server.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_agent(ssh_session session,
Packit 6c0a39
                       const char *username) {
Packit 6c0a39
    int rc = SSH_AUTH_ERROR;
Packit 6c0a39
    struct ssh_agent_state_struct *state;
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (!ssh_agent_is_running(session)) {
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (!session->agent_state) {
Packit 6c0a39
        session->agent_state = malloc(sizeof(struct ssh_agent_state_struct));
Packit 6c0a39
        if (!session->agent_state) {
Packit 6c0a39
            ssh_set_error_oom(session);
Packit 6c0a39
            return SSH_AUTH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
        ZERO_STRUCTP(session->agent_state);
Packit 6c0a39
        session->agent_state->state=SSH_AGENT_STATE_NONE;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    state = session->agent_state;
Packit 6c0a39
    if (state->pubkey == NULL) {
Packit 6c0a39
        state->pubkey = ssh_agent_get_first_ident(session, &state->comment);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (state->pubkey == NULL) {
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    while (state->pubkey != NULL) {
Packit 6c0a39
        if (state->state == SSH_AGENT_STATE_NONE) {
Packit 6c0a39
            SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                    "Trying identity %s", state->comment);
Packit 6c0a39
        }
Packit 6c0a39
        if (state->state == SSH_AGENT_STATE_NONE ||
Packit 6c0a39
                state->state == SSH_AGENT_STATE_PUBKEY) {
Packit 6c0a39
            rc = ssh_userauth_try_publickey(session, username, state->pubkey);
Packit 6c0a39
            if (rc == SSH_AUTH_ERROR) {
Packit 6c0a39
                ssh_agent_state_free (state);
Packit 6c0a39
                session->agent_state = NULL;
Packit 6c0a39
                return rc;
Packit 6c0a39
            } else if (rc == SSH_AUTH_AGAIN) {
Packit 6c0a39
                state->state = SSH_AGENT_STATE_PUBKEY;
Packit 6c0a39
                return rc;
Packit 6c0a39
            } else if (rc != SSH_AUTH_SUCCESS) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                        "Public key of %s refused by server", state->comment);
Packit 6c0a39
                ssh_string_free_char(state->comment);
Packit 6c0a39
                state->comment = NULL;
Packit 6c0a39
                ssh_key_free(state->pubkey);
Packit 6c0a39
                state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
Packit 6c0a39
                state->state = SSH_AGENT_STATE_NONE;
Packit 6c0a39
                continue;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                    "Public key of %s accepted by server", state->comment);
Packit 6c0a39
            state->state = SSH_AGENT_STATE_AUTH;
Packit 6c0a39
        }
Packit 6c0a39
        if (state->state == SSH_AGENT_STATE_AUTH) {
Packit 6c0a39
            rc = ssh_userauth_agent_publickey(session, username, state->pubkey);
Packit 6c0a39
            if (rc == SSH_AUTH_AGAIN)
Packit 6c0a39
                return rc;
Packit 6c0a39
            ssh_string_free_char(state->comment);
Packit 6c0a39
            state->comment = NULL;
Packit 6c0a39
            if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_PARTIAL) {
Packit 6c0a39
                ssh_agent_state_free (session->agent_state);
Packit 6c0a39
                session->agent_state = NULL;
Packit 6c0a39
                return rc;
Packit 6c0a39
            } else if (rc != SSH_AUTH_SUCCESS) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
                        "Server accepted public key but refused the signature");
Packit 6c0a39
                ssh_key_free(state->pubkey);
Packit 6c0a39
                state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
Packit 6c0a39
                state->state = SSH_AGENT_STATE_NONE;
Packit 6c0a39
                continue;
Packit 6c0a39
            }
Packit 6c0a39
            ssh_agent_state_free (session->agent_state);
Packit 6c0a39
            session->agent_state = NULL;
Packit 6c0a39
            return SSH_AUTH_SUCCESS;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    ssh_agent_state_free (session->agent_state);
Packit 6c0a39
    session->agent_state = NULL;
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
enum ssh_auth_auto_state_e {
Packit 6c0a39
    SSH_AUTH_AUTO_STATE_NONE = 0,
Packit 6c0a39
    SSH_AUTH_AUTO_STATE_PUBKEY,
Packit 6c0a39
    SSH_AUTH_AUTO_STATE_KEY_IMPORTED,
Packit 6c0a39
    SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
struct ssh_auth_auto_state_struct {
Packit 6c0a39
    enum ssh_auth_auto_state_e state;
Packit 6c0a39
    struct ssh_iterator *it;
Packit 6c0a39
    ssh_key privkey;
Packit 6c0a39
    ssh_key pubkey;
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Tries to automatically authenticate with public key and "none"
Packit 6c0a39
 *
Packit 6c0a39
 * It may fail, for instance it doesn't ask for a password and uses a default
Packit 6c0a39
 * asker for passphrases (in case the private key is encrypted).
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session     The SSH session.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  username    The username, this SHOULD be NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  passphrase  Use this passphrase to unlock the privatekey. Use NULL
Packit 6c0a39
 *                         if you don't want to use a passphrase or the user
Packit 6c0a39
 *                         should be asked.
Packit 6c0a39
 *
Packit 6c0a39
 * @return  SSH_AUTH_ERROR:   A serious error happened.\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  The server doesn't accept that public key as an
Packit 6c0a39
 *                            authentication token. Try another key or another
Packit 6c0a39
 *                            method.\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method.\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: The public key is accepted, you want now to use
Packit 6c0a39
 *                            ssh_userauth_publickey().\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
Packit 6c0a39
 * @note Most server implementations do not permit changing the username during
Packit 6c0a39
 * authentication. The username should only be set with ssh_options_set() only
Packit 6c0a39
 * before you connect to the server.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_publickey_auto(ssh_session session,
Packit 6c0a39
                                const char *username,
Packit 6c0a39
                                const char *passphrase)
Packit 6c0a39
{
Packit 6c0a39
    ssh_auth_callback auth_fn = NULL;
Packit 6c0a39
    void *auth_data = NULL;
Packit 6c0a39
    struct ssh_auth_auto_state_struct *state;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    if (! (session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH)) {
Packit 6c0a39
        session->auth.supported_methods &= ~SSH_AUTH_METHOD_PUBLICKEY;
Packit 6c0a39
        return SSH_AUTH_DENIED;
Packit 6c0a39
    }
Packit 6c0a39
    if (session->common.callbacks) {
Packit 6c0a39
        auth_fn = session->common.callbacks->auth_function;
Packit 6c0a39
        auth_data = session->common.callbacks->userdata;
Packit 6c0a39
    }
Packit 6c0a39
    if (!session->auth.auto_state) {
Packit 6c0a39
        session->auth.auto_state =
Packit 6c0a39
                calloc(1, sizeof(struct ssh_auth_auto_state_struct));
Packit 6c0a39
        if (!session->auth.auto_state) {
Packit 6c0a39
            ssh_set_error_oom(session);
Packit 6c0a39
            return SSH_AUTH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
    state = session->auth.auto_state;
Packit 6c0a39
    if (state->state == SSH_AUTH_AUTO_STATE_NONE) {
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
        /* Try authentication with ssh-agent first */
Packit 6c0a39
        rc = ssh_userauth_agent(session, username);
Packit 6c0a39
        if (rc == SSH_AUTH_SUCCESS ||
Packit 6c0a39
            rc == SSH_AUTH_PARTIAL ||
Packit 6c0a39
            rc == SSH_AUTH_AGAIN ) {
Packit 6c0a39
            return rc;
Packit 6c0a39
        }
Packit 6c0a39
#endif
Packit 6c0a39
        state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
Packit 6c0a39
    }
Packit 6c0a39
    if (state->it == NULL) {
Packit 6c0a39
        state->it = ssh_list_get_iterator(session->opts.identity);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    while (state->it != NULL) {
Packit 6c0a39
        const char *privkey_file = state->it->data;
Packit 6c0a39
        char pubkey_file[1024] = {0};
Packit 6c0a39
        if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY) {
Packit 6c0a39
            SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                    "Trying to authenticate with %s", privkey_file);
Packit 6c0a39
            state->privkey = NULL;
Packit 6c0a39
            state->pubkey = NULL;
Packit 6c0a39
            snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file);
Packit 6c0a39
Packit 6c0a39
            rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey);
Packit 6c0a39
            if (rc == SSH_ERROR) {
Packit 6c0a39
                ssh_set_error(session,
Packit 6c0a39
                        SSH_FATAL,
Packit 6c0a39
                        "Failed to import public key: %s",
Packit 6c0a39
                        pubkey_file);
Packit 6c0a39
                SAFE_FREE(session->auth.auto_state);
Packit 6c0a39
                return SSH_AUTH_ERROR;
Packit 6c0a39
            } else if (rc == SSH_EOF) {
Packit 6c0a39
                /* Read the private key and save the public key to file */
Packit 6c0a39
                rc = ssh_pki_import_privkey_file(privkey_file,
Packit 6c0a39
                        passphrase,
Packit 6c0a39
                        auth_fn,
Packit 6c0a39
                        auth_data,
Packit 6c0a39
                        &state->privkey);
Packit 6c0a39
                if (rc == SSH_ERROR) {
Packit 6c0a39
                    ssh_set_error(session,
Packit 6c0a39
                            SSH_FATAL,
Packit 6c0a39
                            "Failed to read private key: %s",
Packit 6c0a39
                            privkey_file);
Packit 6c0a39
                    state->it=state->it->next;
Packit 6c0a39
                    continue;
Packit 6c0a39
                } else if (rc == SSH_EOF) {
Packit 6c0a39
                    /* If the file doesn't exist, continue */
Packit 6c0a39
                    SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                            "Private key %s doesn't exist.",
Packit 6c0a39
                            privkey_file);
Packit 6c0a39
                    state->it=state->it->next;
Packit 6c0a39
                    continue;
Packit 6c0a39
                }
Packit 6c0a39
Packit 6c0a39
                rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey);
Packit 6c0a39
                if (rc == SSH_ERROR) {
Packit 6c0a39
                    ssh_key_free(state->privkey);
Packit 6c0a39
                    SAFE_FREE(session->auth.auto_state);
Packit 6c0a39
                    return SSH_AUTH_ERROR;
Packit 6c0a39
                }
Packit 6c0a39
Packit 6c0a39
                rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file);
Packit 6c0a39
                if (rc == SSH_ERROR) {
Packit 6c0a39
                    SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                            "Could not write public key to file: %s",
Packit 6c0a39
                            pubkey_file);
Packit 6c0a39
                }
Packit 6c0a39
            }
Packit 6c0a39
            state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED;
Packit 6c0a39
        }
Packit 6c0a39
        if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
Packit 6c0a39
            rc = ssh_userauth_try_publickey(session, username, state->pubkey);
Packit 6c0a39
            if (rc == SSH_AUTH_ERROR) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "Public key authentication error for %s",
Packit 6c0a39
                        privkey_file);
Packit 6c0a39
                ssh_key_free(state->privkey);
Packit 6c0a39
                ssh_key_free(state->pubkey);
Packit 6c0a39
                SAFE_FREE(session->auth.auto_state);
Packit 6c0a39
                return rc;
Packit 6c0a39
            } else if (rc == SSH_AUTH_AGAIN) {
Packit 6c0a39
                return rc;
Packit 6c0a39
            } else if (rc != SSH_AUTH_SUCCESS) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
                        "Public key for %s refused by server",
Packit 6c0a39
                        privkey_file);
Packit 6c0a39
                ssh_key_free(state->privkey);
Packit 6c0a39
                state->privkey = NULL;
Packit 6c0a39
                ssh_key_free(state->pubkey);
Packit 6c0a39
                state->pubkey = NULL;
Packit 6c0a39
                state->it=state->it->next;
Packit 6c0a39
                state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
Packit 6c0a39
                continue;
Packit 6c0a39
            }
Packit 6c0a39
            state->state = SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED;
Packit 6c0a39
        }
Packit 6c0a39
        if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED) {
Packit 6c0a39
            /* Public key has been accepted by the server */
Packit 6c0a39
            if (state->privkey == NULL) {
Packit 6c0a39
                rc = ssh_pki_import_privkey_file(privkey_file,
Packit 6c0a39
                        passphrase,
Packit 6c0a39
                        auth_fn,
Packit 6c0a39
                        auth_data,
Packit 6c0a39
                        &state->privkey);
Packit 6c0a39
                if (rc == SSH_ERROR) {
Packit 6c0a39
                    ssh_key_free(state->pubkey);
Packit 6c0a39
                    state->pubkey=NULL;
Packit 6c0a39
                    ssh_set_error(session,
Packit 6c0a39
                            SSH_FATAL,
Packit 6c0a39
                            "Failed to read private key: %s",
Packit 6c0a39
                            privkey_file);
Packit 6c0a39
                    state->it=state->it->next;
Packit 6c0a39
                    state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
Packit 6c0a39
                    continue;
Packit 6c0a39
                } else if (rc == SSH_EOF) {
Packit 6c0a39
                    /* If the file doesn't exist, continue */
Packit 6c0a39
                    ssh_key_free(state->pubkey);
Packit 6c0a39
                    state->pubkey = NULL;
Packit 6c0a39
                    SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
                            "Private key %s doesn't exist.",
Packit 6c0a39
                            privkey_file);
Packit 6c0a39
                    state->it = state->it->next;
Packit 6c0a39
                    state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
Packit 6c0a39
                    continue;
Packit 6c0a39
                }
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            rc = ssh_userauth_publickey(session, username, state->privkey);
Packit 6c0a39
            if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) {
Packit 6c0a39
                ssh_key_free(state->privkey);
Packit 6c0a39
                ssh_key_free(state->pubkey);
Packit 6c0a39
                SAFE_FREE(session->auth.auto_state);
Packit 6c0a39
                if (rc == SSH_AUTH_SUCCESS) {
Packit 6c0a39
                    SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
                            "Successfully authenticated using %s",
Packit 6c0a39
                            privkey_file);
Packit 6c0a39
                }
Packit 6c0a39
                return rc;
Packit 6c0a39
            }
Packit 6c0a39
            if (rc == SSH_AUTH_AGAIN) {
Packit 6c0a39
                return rc;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                    "The server accepted the public key but refused the signature");
Packit 6c0a39
            state->it = state->it->next;
Packit 6c0a39
            state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
Packit 6c0a39
            /* continue */
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
    SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
            "Tried every public key, none matched");
Packit 6c0a39
    SAFE_FREE(session->auth.auto_state);
Packit 6c0a39
    return SSH_AUTH_DENIED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Try to authenticate by password.
Packit 6c0a39
 *
Packit 6c0a39
 * This authentication method is normally disabled on SSHv2 server. You should
Packit 6c0a39
 * use keyboard-interactive mode.
Packit 6c0a39
 *
Packit 6c0a39
 * The 'password' value MUST be encoded UTF-8.  It is up to the server how to
Packit 6c0a39
 * interpret the password and validate it against the password database.
Packit 6c0a39
 * However, if you read the password in some other encoding, you MUST convert
Packit 6c0a39
 * the password to UTF-8.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] session   The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] username  The username, this SHOULD be NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in] password  The password to authenticate in UTF-8.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_AUTH_ERROR:   A serious error happened.\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  Authentication failed: use another method\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: Authentication success\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
Packit 6c0a39
 * @note Most server implementations do not permit changing the username during
Packit 6c0a39
 * authentication. The username should only be set with ssh_options_set() only
Packit 6c0a39
 * before you connect to the server.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_userauth_none()
Packit 6c0a39
 * @see ssh_userauth_kbdint()
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_password(ssh_session session,
Packit 6c0a39
                          const char *username,
Packit 6c0a39
                          const char *password) {
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    switch(session->pending_call_state) {
Packit 6c0a39
        case SSH_PENDING_CALL_NONE:
Packit 6c0a39
            break;
Packit 6c0a39
        case SSH_PENDING_CALL_AUTH_PASSWORD:
Packit 6c0a39
            goto pending;
Packit 6c0a39
        default:
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_FATAL,
Packit 6c0a39
                          "Wrong state (%d) during pending SSH call",
Packit 6c0a39
                          session->pending_call_state);
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    } else if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* request */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bsssbs",
Packit 6c0a39
            SSH2_MSG_USERAUTH_REQUEST,
Packit 6c0a39
            username ? username : session->opts.username,
Packit 6c0a39
            "ssh-connection",
Packit 6c0a39
            "password",
Packit 6c0a39
            0, /* false */
Packit 6c0a39
            password
Packit 6c0a39
    );
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Set the buffer as secure to be explicitly zeroed when freed */
Packit 6c0a39
    ssh_buffer_set_secure(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_PASSWORD;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_PASSWORD_AUTH_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_PASSWORD;
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN) {
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
/* LEGACY */
Packit 6c0a39
int ssh_userauth_agent_pubkey(ssh_session session,
Packit 6c0a39
                              const char *username,
Packit 6c0a39
                              ssh_public_key publickey)
Packit 6c0a39
{
Packit 6c0a39
    ssh_key key;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    key = ssh_key_new();
Packit 6c0a39
    if (key == NULL) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    key->type = publickey->type;
Packit 6c0a39
    key->type_c = ssh_key_type_to_char(key->type);
Packit 6c0a39
    key->flags = SSH_KEY_FLAG_PUBLIC;
Packit 6c0a39
    key->dsa = publickey->dsa_pub;
Packit 6c0a39
    key->rsa = publickey->rsa_pub;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_agent_publickey(session, username, key);
Packit 6c0a39
Packit 6c0a39
    key->dsa = NULL;
Packit 6c0a39
    key->rsa = NULL;
Packit 6c0a39
    ssh_key_free(key);
Packit 6c0a39
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
#endif /* _WIN32 */
Packit 6c0a39
Packit 6c0a39
ssh_kbdint ssh_kbdint_new(void) {
Packit 6c0a39
    ssh_kbdint kbd;
Packit 6c0a39
Packit 6c0a39
    kbd = calloc(1, sizeof(struct ssh_kbdint_struct));
Packit 6c0a39
    if (kbd == NULL) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return kbd;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
void ssh_kbdint_free(ssh_kbdint kbd) {
Packit 6c0a39
    int i, n;
Packit 6c0a39
Packit 6c0a39
    if (kbd == NULL) {
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SAFE_FREE(kbd->name);
Packit 6c0a39
    SAFE_FREE(kbd->instruction);
Packit 6c0a39
    SAFE_FREE(kbd->echo);
Packit 6c0a39
Packit 6c0a39
    n = kbd->nprompts;
Packit 6c0a39
    if (kbd->prompts) {
Packit 6c0a39
        for (i = 0; i < n; i++) {
Packit 6c0a39
            if (kbd->prompts[i] != NULL) {
Packit 6c0a39
                explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i]));
Packit 6c0a39
            }
Packit 6c0a39
            SAFE_FREE(kbd->prompts[i]);
Packit 6c0a39
        }
Packit 6c0a39
        SAFE_FREE(kbd->prompts);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    n = kbd->nanswers;
Packit 6c0a39
    if (kbd->answers) {
Packit 6c0a39
        for (i = 0; i < n; i++) {
Packit 6c0a39
            if (kbd->answers[i] != NULL) {
Packit 6c0a39
                explicit_bzero(kbd->answers[i], strlen(kbd->answers[i]));
Packit 6c0a39
            }
Packit 6c0a39
            SAFE_FREE(kbd->answers[i]);
Packit 6c0a39
        }
Packit 6c0a39
        SAFE_FREE(kbd->answers);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SAFE_FREE(kbd);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
void ssh_kbdint_clean(ssh_kbdint kbd) {
Packit 6c0a39
    int i, n;
Packit 6c0a39
Packit 6c0a39
    if (kbd == NULL) {
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SAFE_FREE(kbd->name);
Packit 6c0a39
    SAFE_FREE(kbd->instruction);
Packit 6c0a39
    SAFE_FREE(kbd->echo);
Packit 6c0a39
Packit 6c0a39
    n = kbd->nprompts;
Packit 6c0a39
    if (kbd->prompts) {
Packit 6c0a39
        for (i = 0; i < n; i++) {
Packit 6c0a39
            explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i]));
Packit 6c0a39
            SAFE_FREE(kbd->prompts[i]);
Packit 6c0a39
        }
Packit 6c0a39
        SAFE_FREE(kbd->prompts);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    n = kbd->nanswers;
Packit 6c0a39
Packit 6c0a39
    if (kbd->answers) {
Packit 6c0a39
        for (i = 0; i < n; i++) {
Packit 6c0a39
            explicit_bzero(kbd->answers[i], strlen(kbd->answers[i]));
Packit 6c0a39
            SAFE_FREE(kbd->answers[i]);
Packit 6c0a39
        }
Packit 6c0a39
        SAFE_FREE(kbd->answers);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    kbd->nprompts = 0;
Packit 6c0a39
    kbd->nanswers = 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/*
Packit 6c0a39
 * This function sends the first packet as explained in RFC 3066 section 3.1.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_userauth_kbdint_init(ssh_session session,
Packit 6c0a39
                                    const char *username,
Packit 6c0a39
                                    const char *submethods)
Packit 6c0a39
{
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT) {
Packit 6c0a39
        goto pending;
Packit 6c0a39
    }
Packit 6c0a39
    if (session->pending_call_state != SSH_PENDING_CALL_NONE) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    }
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* request */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bsssss",
Packit 6c0a39
            SSH2_MSG_USERAUTH_REQUEST,
Packit 6c0a39
            username ? username : session->opts.username,
Packit 6c0a39
            "ssh-connection",
Packit 6c0a39
            "keyboard-interactive",
Packit 6c0a39
            "", /* lang (ignore it) */
Packit 6c0a39
            submethods ? submethods : ""
Packit 6c0a39
    );
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_KBDINT_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_INIT;
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
            "Sending keyboard-interactive init request");
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN)
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Send the current challenge response and wait for a reply from the
Packit 6c0a39
 *        server.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_AUTH_INFO if more info is needed
Packit 6c0a39
 * @returns SSH_AUTH_SUCCESS
Packit 6c0a39
 * @returns SSH_AUTH_FAILURE
Packit 6c0a39
 * @returns SSH_AUTH_PARTIAL
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_userauth_kbdint_send(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    uint32_t i;
Packit 6c0a39
    int rc;
Packit 6c0a39
    if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND)
Packit 6c0a39
        goto pending;
Packit 6c0a39
    if (session->pending_call_state != SSH_PENDING_CALL_NONE) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer, "bd",
Packit 6c0a39
            SSH2_MSG_USERAUTH_INFO_RESPONSE,
Packit 6c0a39
            session->kbdint->nprompts);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        goto fail;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (i = 0; i < session->kbdint->nprompts; i++) {
Packit 6c0a39
        rc = ssh_buffer_pack(session->out_buffer, "s",
Packit 6c0a39
                session->kbdint->answers && session->kbdint->answers[i] ?
Packit 6c0a39
                        session->kbdint->answers[i]:"");
Packit 6c0a39
        if (rc < 0) {
Packit 6c0a39
            goto fail;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_INTERACTIVE;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_KBDINT_SENT;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_SEND;
Packit 6c0a39
    ssh_kbdint_free(session->kbdint);
Packit 6c0a39
    session->kbdint = NULL;
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
            "Sending keyboard-interactive response packet");
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN)
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    return rc;
Packit 6c0a39
fail:
Packit 6c0a39
    ssh_set_error_oom(session);
Packit 6c0a39
    ssh_buffer_reinit(session->out_buffer);
Packit 6c0a39
Packit 6c0a39
    return SSH_AUTH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief handles a SSH_USERAUTH_INFO_REQUEST packet, as used in
Packit 6c0a39
 *        keyboard-interactive authentication, and changes the
Packit 6c0a39
 *        authentication state.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
Packit 6c0a39
    ssh_string tmp = NULL;
Packit 6c0a39
    uint32_t nprompts;
Packit 6c0a39
    uint32_t i;
Packit 6c0a39
    int rc;
Packit 6c0a39
    (void)user;
Packit 6c0a39
    (void)type;
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
    if (session->kbdint == NULL) {
Packit 6c0a39
        session->kbdint = ssh_kbdint_new();
Packit 6c0a39
        if (session->kbdint == NULL) {
Packit 6c0a39
            ssh_set_error_oom(session);
Packit 6c0a39
            return SSH_PACKET_USED;
Packit 6c0a39
        }
Packit 6c0a39
    } else {
Packit 6c0a39
        ssh_kbdint_clean(session->kbdint);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_unpack(packet, "ssSd",
Packit 6c0a39
            &session->kbdint->name, /* name of the "asking" window shown to client */
Packit 6c0a39
            &session->kbdint->instruction,
Packit 6c0a39
            &tmp, /* to ignore */
Packit 6c0a39
            &nprompts
Packit 6c0a39
            );
Packit 6c0a39
Packit 6c0a39
    /* We don't care about tmp */
Packit 6c0a39
    ssh_string_free(tmp);
Packit 6c0a39
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg");
Packit 6c0a39
        ssh_kbdint_free(session->kbdint);
Packit 6c0a39
        session->kbdint = NULL;
Packit 6c0a39
        return SSH_PACKET_USED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_DEBUG,
Packit 6c0a39
            "%d keyboard-interactive prompts", nprompts);
Packit 6c0a39
    if (nprompts > KBDINT_MAX_PROMPT) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                "Too much prompts requested by the server: %u (0x%.4x)",
Packit 6c0a39
                nprompts, nprompts);
Packit 6c0a39
        ssh_kbdint_free(session->kbdint);
Packit 6c0a39
        session->kbdint = NULL;
Packit 6c0a39
Packit 6c0a39
        return SSH_PACKET_USED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->kbdint->nprompts = nprompts;
Packit 6c0a39
    session->kbdint->nanswers = nprompts;
Packit 6c0a39
    session->kbdint->prompts = calloc(nprompts, sizeof(char *));
Packit 6c0a39
    if (session->kbdint->prompts == NULL) {
Packit 6c0a39
        session->kbdint->nprompts = 0;
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        ssh_kbdint_free(session->kbdint);
Packit 6c0a39
        session->kbdint = NULL;
Packit 6c0a39
Packit 6c0a39
        return SSH_PACKET_USED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->kbdint->echo = calloc(nprompts, sizeof(unsigned char));
Packit 6c0a39
    if (session->kbdint->echo == NULL) {
Packit 6c0a39
        session->kbdint->nprompts = 0;
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        ssh_kbdint_free(session->kbdint);
Packit 6c0a39
        session->kbdint = NULL;
Packit 6c0a39
Packit 6c0a39
        return SSH_PACKET_USED;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (i = 0; i < nprompts; i++) {
Packit 6c0a39
        rc = ssh_buffer_unpack(packet, "sb",
Packit 6c0a39
                &session->kbdint->prompts[i],
Packit 6c0a39
                &session->kbdint->echo[i]);
Packit 6c0a39
        if (rc == SSH_ERROR) {
Packit 6c0a39
            ssh_set_error(session, SSH_FATAL, "Short INFO_REQUEST packet");
Packit 6c0a39
            ssh_kbdint_free(session->kbdint);
Packit 6c0a39
            session->kbdint = NULL;
Packit 6c0a39
Packit 6c0a39
            return SSH_PACKET_USED;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
    session->auth.state=SSH_AUTH_STATE_INFO;
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Try to authenticate through the "keyboard-interactive" method.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  user     The username to authenticate. You can specify NULL if
Packit 6c0a39
 *                      ssh_option_set_username() has been used. You cannot try
Packit 6c0a39
 *                      two different logins in a row.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  submethods Undocumented. Set it to NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_AUTH_ERROR:   A serious error happened\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  Authentication failed : use another method\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: Authentication success\n
Packit 6c0a39
 *          SSH_AUTH_INFO:    The server asked some questions. Use
Packit 6c0a39
 *                            ssh_userauth_kbdint_getnprompts() and such.\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 *
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
int ssh_userauth_kbdint(ssh_session session, const char *user,
Packit 6c0a39
    const char *submethods) {
Packit 6c0a39
    int rc = SSH_AUTH_ERROR;
Packit 6c0a39
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if ((session->pending_call_state == SSH_PENDING_CALL_NONE && session->kbdint == NULL) ||
Packit 6c0a39
            session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT)
Packit 6c0a39
        rc = ssh_userauth_kbdint_init(session, user, submethods);
Packit 6c0a39
    else if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND ||
Packit 6c0a39
            session->kbdint != NULL) {
Packit 6c0a39
        /*
Packit 6c0a39
         * If we are at this point, it is because session->kbdint exists.
Packit 6c0a39
         * It means the user has set some information there we need to send
Packit 6c0a39
         * the server and then we need to ack the status (new questions or ok
Packit 6c0a39
         * pass in).
Packit 6c0a39
         * It is possible that session->kbdint is NULL while we're waiting for
Packit 6c0a39
         * a reply, hence the test for the pending call.
Packit 6c0a39
         */
Packit 6c0a39
        rc = ssh_userauth_kbdint_send(session);
Packit 6c0a39
    } else {
Packit 6c0a39
        /* We are here because session->kbdint == NULL & state != NONE.
Packit 6c0a39
         * This should not happen
Packit 6c0a39
         */
Packit 6c0a39
        rc = SSH_AUTH_ERROR;
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid state in %s", __func__);
Packit 6c0a39
    }
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the number of prompts (questions) the server has given.
Packit 6c0a39
 *
Packit 6c0a39
 * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return
Packit 6c0a39
 * code, this function can be used to retrieve information about the keyboard
Packit 6c0a39
 * interactive authentication questions sent by the remote host.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns             The number of prompts.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_kbdint_getnprompts(ssh_session session) {
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    if (session->kbdint == NULL) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    return session->kbdint->nprompts;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the "name" of the message block.
Packit 6c0a39
 *
Packit 6c0a39
 * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return
Packit 6c0a39
 * code, this function can be used to retrieve information about the keyboard
Packit 6c0a39
 * interactive authentication questions sent by the remote host.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns             The name of the message block. Do not free it.
Packit 6c0a39
 */
Packit 6c0a39
const char *ssh_userauth_kbdint_getname(ssh_session session) {
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    if (session->kbdint == NULL) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    return session->kbdint->name;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the "instruction" of the message block.
Packit 6c0a39
 *
Packit 6c0a39
 * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return
Packit 6c0a39
 * code, this function can be used to retrieve information about the keyboard
Packit 6c0a39
 * interactive authentication questions sent by the remote host.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns             The instruction of the message block.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
Packit 6c0a39
    if (session == NULL)
Packit 6c0a39
        return NULL;
Packit 6c0a39
    if (session->kbdint == NULL) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    return session->kbdint->instruction;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get a prompt from a message block.
Packit 6c0a39
 *
Packit 6c0a39
 * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return
Packit 6c0a39
 * code, this function can be used to retrieve information about the keyboard
Packit 6c0a39
 * interactive authentication questions sent by the remote host.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  i        The index number of the i'th prompt.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[out] echo     This is an optional variable. You can obtain a
Packit 6c0a39
 *                      boolean if the user input should be echoed or
Packit 6c0a39
 *                      hidden. For passwords it is usually hidden.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns             A pointer to the prompt. Do not free it.
Packit 6c0a39
 *
Packit 6c0a39
 * @code
Packit 6c0a39
 *   const char prompt;
Packit 6c0a39
 *   char echo;
Packit 6c0a39
 *
Packit 6c0a39
 *   prompt = ssh_userauth_kbdint_getprompt(session, 0, &echo);
Packit 6c0a39
 *   if (echo) ...
Packit 6c0a39
 * @endcode
Packit 6c0a39
 */
Packit 6c0a39
const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
Packit 6c0a39
    char *echo) {
Packit 6c0a39
    if (session == NULL)
Packit 6c0a39
        return NULL;
Packit 6c0a39
    if (session->kbdint == NULL) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    if (i > session->kbdint->nprompts) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (echo) {
Packit 6c0a39
        *echo = session->kbdint->echo[i];
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return session->kbdint->prompts[i];
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifdef WITH_SERVER
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the number of answers the client has given.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns             The number of answers.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_kbdint_getnanswers(ssh_session session) {
Packit 6c0a39
    if (session == NULL || session->kbdint == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    return session->kbdint->nanswers;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the answer for a question from a message block.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  i index  The number of the ith answer.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 on success, < 0 on error.
Packit 6c0a39
 */
Packit 6c0a39
const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
Packit 6c0a39
    if (session == NULL || session->kbdint == NULL
Packit 6c0a39
            || session->kbdint->answers == NULL) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
    if (i >= session->kbdint->nanswers) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return session->kbdint->answers[i];
Packit 6c0a39
}
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Set the answer for a question from a message block.
Packit 6c0a39
 *
Packit 6c0a39
 * If you have called ssh_userauth_kbdint() and got SSH_AUTH_INFO, this
Packit 6c0a39
 * function returns the questions from the server.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  i index  The number of the ith prompt.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  answer   The answer to give to the server. The answer MUST be
Packit 6c0a39
 *                      encoded UTF-8. It is up to the server how to interpret
Packit 6c0a39
 *                      the value and validate it. However, if you read the
Packit 6c0a39
 *                      answer in some other encoding, you MUST convert it to
Packit 6c0a39
 *                      UTF-8.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 on success, < 0 on error.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
Packit 6c0a39
    const char *answer) {
Packit 6c0a39
    if (session == NULL) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
    if (answer == NULL || session->kbdint == NULL ||
Packit 6c0a39
            i >= session->kbdint->nprompts) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->kbdint->answers == NULL) {
Packit 6c0a39
        session->kbdint->answers = calloc(session->kbdint->nprompts, sizeof(char *));
Packit 6c0a39
        if (session->kbdint->answers == NULL) {
Packit 6c0a39
            ssh_set_error_oom(session);
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->kbdint->answers[i]) {
Packit 6c0a39
        explicit_bzero(session->kbdint->answers[i],
Packit 6c0a39
                strlen(session->kbdint->answers[i]));
Packit 6c0a39
        SAFE_FREE(session->kbdint->answers[i]);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->kbdint->answers[i] = strdup(answer);
Packit 6c0a39
    if (session->kbdint->answers[i] == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Try to authenticate through the "gssapi-with-mic" method.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_AUTH_ERROR:   A serious error happened\n
Packit 6c0a39
 *          SSH_AUTH_DENIED:  Authentication failed : use another method\n
Packit 6c0a39
 *          SSH_AUTH_PARTIAL: You've been partially authenticated, you still
Packit 6c0a39
 *                            have to use another method\n
Packit 6c0a39
 *          SSH_AUTH_SUCCESS: Authentication success\n
Packit 6c0a39
 *          SSH_AUTH_AGAIN:   In nonblocking mode, you've got to call this again
Packit 6c0a39
 *                            later.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_userauth_gssapi(ssh_session session) {
Packit 6c0a39
    int rc = SSH_AUTH_DENIED;
Packit 6c0a39
#ifdef WITH_GSSAPI
Packit 6c0a39
    switch(session->pending_call_state) {
Packit 6c0a39
    case SSH_PENDING_CALL_NONE:
Packit 6c0a39
        break;
Packit 6c0a39
    case SSH_PENDING_CALL_AUTH_GSSAPI_MIC:
Packit 6c0a39
        goto pending;
Packit 6c0a39
    default:
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                SSH_FATAL,
Packit 6c0a39
                "Wrong state (%d) during pending SSH call",
Packit 6c0a39
                session->pending_call_state);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_userauth_request_service(session);
Packit 6c0a39
    if (rc == SSH_AGAIN) {
Packit 6c0a39
        return SSH_AUTH_AGAIN;
Packit 6c0a39
    } else if (rc == SSH_ERROR) {
Packit 6c0a39
        return SSH_AUTH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi-with-mic");
Packit 6c0a39
Packit 6c0a39
    session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_MIC;
Packit 6c0a39
    session->auth.state = SSH_AUTH_STATE_NONE;
Packit 6c0a39
    session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_MIC;
Packit 6c0a39
    rc = ssh_gssapi_auth_mic(session);
Packit 6c0a39
Packit 6c0a39
    if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_DENIED) {
Packit 6c0a39
        session->auth.state = SSH_AUTH_STATE_NONE;
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
pending:
Packit 6c0a39
    rc = ssh_userauth_get_response(session);
Packit 6c0a39
    if (rc != SSH_AUTH_AGAIN) {
Packit 6c0a39
        session->pending_call_state = SSH_PENDING_CALL_NONE;
Packit 6c0a39
    }
Packit 6c0a39
#else
Packit 6c0a39
    (void) session; /* unused */
Packit 6c0a39
#endif
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @} */