diff options
Diffstat (limited to 'libssh/src/auth.c')
-rw-r--r-- | libssh/src/auth.c | 1810 |
1 files changed, 0 insertions, 1810 deletions
diff --git a/libssh/src/auth.c b/libssh/src/auth.c deleted file mode 100644 index d56111d2..00000000 --- a/libssh/src/auth.c +++ /dev/null @@ -1,1810 +0,0 @@ -/* - * auth.c - Authentication with SSH protocols - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis <aris@0xbadc0de.be> - * Copyright (c) 2008-2013 Andreas Schneider <asn@cryptomilk.org> - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#ifndef _WIN32 -#include <netinet/in.h> -#include <arpa/inet.h> -#endif - -#include "libssh/priv.h" -#include "libssh/crypto.h" -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/agent.h" -#include "libssh/misc.h" -#include "libssh/packet.h" -#include "libssh/session.h" -#include "libssh/keys.h" -#include "libssh/auth.h" -#include "libssh/pki.h" -#include "libssh/gssapi.h" -#include "libssh/legacy.h" - -/** - * @defgroup libssh_auth The SSH authentication functions. - * @ingroup libssh - * - * Functions to authenticate with a server. - * - * @{ - */ - -/** - * @internal - * - * @brief Ask access to the ssh-userauth service. - * - * @param[in] session The SSH session handle. - * - * @returns SSH_OK on success, SSH_ERROR on error. - * @returns SSH_AGAIN on nonblocking mode, if calling that function - * again is necessary - */ -static int ssh_userauth_request_service(ssh_session session) { - int rc; - - rc = ssh_service_request(session, "ssh-userauth"); - if (rc != SSH_OK) { - SSH_LOG(SSH_LOG_WARN, - "Failed to request \"ssh-userauth\" service"); - } - - return rc; -} - -static int ssh_auth_response_termination(void *user){ - ssh_session session=(ssh_session)user; - switch(session->auth_state){ - case SSH_AUTH_STATE_NONE: - case SSH_AUTH_STATE_KBDINT_SENT: - case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: - case SSH_AUTH_STATE_GSSAPI_TOKEN: - case SSH_AUTH_STATE_GSSAPI_MIC_SENT: - return 0; - default: - return 1; - } -} - -/** - * @internal - * @brief Wait for a response of an authentication function. - * - * @param[in] session The SSH session. - * - * @returns SSH_AUTH_SUCCESS Authentication success, or pubkey accepted - * SSH_AUTH_PARTIAL Authentication succeeded but another mean - * of authentication is needed. - * SSH_AUTH_INFO Data for keyboard-interactive - * SSH_AUTH_AGAIN In nonblocking mode, call has to be made again - * SSH_AUTH_ERROR Error during the process. - */ -static int ssh_userauth_get_response(ssh_session session) { - int rc = SSH_AUTH_ERROR; - - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, - ssh_auth_response_termination, session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - if (!ssh_auth_response_termination(session)){ - return SSH_AUTH_AGAIN; - } - - switch(session->auth_state) { - case SSH_AUTH_STATE_ERROR: - rc = SSH_AUTH_ERROR; - break; - case SSH_AUTH_STATE_FAILED: - rc = SSH_AUTH_DENIED; - break; - case SSH_AUTH_STATE_INFO: - rc = SSH_AUTH_INFO; - break; - case SSH_AUTH_STATE_PARTIAL: - rc = SSH_AUTH_PARTIAL; - break; - case SSH_AUTH_STATE_PK_OK: - case SSH_AUTH_STATE_SUCCESS: - rc = SSH_AUTH_SUCCESS; - break; - case SSH_AUTH_STATE_KBDINT_SENT: - case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: - case SSH_AUTH_STATE_GSSAPI_TOKEN: - case SSH_AUTH_STATE_GSSAPI_MIC_SENT: - case SSH_AUTH_STATE_NONE: - /* not reached */ - rc = SSH_AUTH_ERROR; - break; - } - - return rc; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_BANNER packet. - * - * This banner should be shown to user prior to authentication - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_banner){ - ssh_string banner; - (void)type; - (void)user; - - banner = buffer_get_ssh_string(packet); - if (banner == NULL) { - SSH_LOG(SSH_LOG_WARN, - "Invalid SSH_USERAUTH_BANNER packet"); - } else { - SSH_LOG(SSH_LOG_DEBUG, - "Received SSH_USERAUTH_BANNER packet"); - if(session->banner != NULL) - ssh_string_free(session->banner); - session->banner = banner; - } - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_FAILURE packet. - * - * This handles the complete or partial authentication failure. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ - char *auth_methods = NULL; - uint8_t partial = 0; - int rc; - (void) type; - (void) user; - - rc = ssh_buffer_unpack(packet, "sb", &auth_methods, &partial); - if (rc != SSH_OK) { - ssh_set_error(session, SSH_FATAL, - "Invalid SSH_MSG_USERAUTH_FAILURE message"); - session->auth_state=SSH_AUTH_STATE_ERROR; - goto end; - } - - if (partial) { - session->auth_state=SSH_AUTH_STATE_PARTIAL; - SSH_LOG(SSH_LOG_INFO, - "Partial success. Authentication that can continue: %s", - auth_methods); - } else { - session->auth_state=SSH_AUTH_STATE_FAILED; - SSH_LOG(SSH_LOG_INFO, - "Access denied. Authentication that can continue: %s", - auth_methods); - ssh_set_error(session, SSH_REQUEST_DENIED, - "Access denied. Authentication that can continue: %s", - auth_methods); - - session->auth_methods = 0; - } - if (strstr(auth_methods, "password") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_PASSWORD; - } - if (strstr(auth_methods, "keyboard-interactive") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; - } - if (strstr(auth_methods, "publickey") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - if (strstr(auth_methods, "hostbased") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_HOSTBASED; - } - if (strstr(auth_methods, "gssapi-with-mic") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_GSSAPI_MIC; - } - -end: - SAFE_FREE(auth_methods); - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_SUCCESS packet. - * - * It is also used to communicate the new to the upper levels. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_success){ - (void)packet; - (void)type; - (void)user; - - SSH_LOG(SSH_LOG_DEBUG, "Authentication successful"); - SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS"); - - session->auth_state=SSH_AUTH_STATE_SUCCESS; - session->session_state=SSH_SESSION_STATE_AUTHENTICATED; - session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; - - if(session->current_crypto && session->current_crypto->delayed_compress_out){ - SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT"); - session->current_crypto->do_compress_out=1; - } - if(session->current_crypto && session->current_crypto->delayed_compress_in){ - SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN"); - session->current_crypto->do_compress_in=1; - } - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_PK_OK or SSH_USERAUTH_INFO_REQUEST packet. - * - * Since the two types of packets share the same code, additional work is done - * to understand if we are in a public key or keyboard-interactive context. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok){ - int rc; - - SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_PK_OK/INFO_REQUEST/GSSAPI_RESPONSE"); - - if(session->auth_state==SSH_AUTH_STATE_KBDINT_SENT){ - /* Assuming we are in keyboard-interactive context */ - SSH_LOG(SSH_LOG_TRACE, - "keyboard-interactive context, assuming SSH_USERAUTH_INFO_REQUEST"); - rc=ssh_packet_userauth_info_request(session,type,packet,user); -#ifdef WITH_GSSAPI - } else if (session->auth_state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT){ - rc = ssh_packet_userauth_gssapi_response(session, type, packet, user); -#endif - } else { - session->auth_state=SSH_AUTH_STATE_PK_OK; - SSH_LOG(SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK"); - rc=SSH_PACKET_USED; - } - - return rc; -} - -/** - * @brief Get available authentication methods from the server. - * - * This requires the function ssh_userauth_none() to be called before the - * methods are available. The server MAY return a list of methods that may - * continue. - * - * @param[in] session The SSH session. - * - * @param[in] username Deprecated, set to NULL. - * - * @returns A bitfield of the fllowing values: - * - SSH_AUTH_METHOD_PASSWORD - * - SSH_AUTH_METHOD_PUBLICKEY - * - SSH_AUTH_METHOD_HOSTBASED - * - SSH_AUTH_METHOD_INTERACTIVE - * - * @warning Other reserved flags may appear in future versions. - * @see ssh_userauth_none() - */ -int ssh_userauth_list(ssh_session session, const char *username) -{ - (void) username; /* unused */ - - if (session == NULL) { - return 0; - } - -#ifdef WITH_SSH1 - if(session->version == 1) { - return SSH_AUTH_METHOD_PASSWORD; - } -#endif - - return session->auth_methods; -} - -/** - * @brief Try to authenticate through the "none" method. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @returns SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: Authentication failed: use another method\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method\n - * SSH_AUTH_SUCCESS: Authentication success\n - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_none(ssh_session session, const char *username) { - int rc; - -#ifdef WITH_SSH1 - if (session->version == 1) { - return ssh_userauth1_none(session, username); - } -#endif - - switch(session->pending_call_state){ - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_NONE: - goto pending; - default: - ssh_set_error(session, SSH_FATAL, - "Wrong state during pending SSH call"); - return SSH_AUTH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsss", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "none" - ); - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_NONE; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -/** - * @brief Try to authenticate with the given public key. - * - * To avoid unnecessary processing and user interaction, the following method - * is provided for querying whether authentication using the 'pubkey' would - * be possible. - * - * @param[in] session The SSH session. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] pubkey The public key to try. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_try_publickey(ssh_session session, - const char *username, - const ssh_key pubkey) -{ - ssh_string pubkey_s = NULL; - int rc; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (pubkey == NULL || !ssh_key_is_public(pubkey)) { - ssh_set_error(session, SSH_FATAL, "Invalid pubkey"); - return SSH_AUTH_ERROR; - } - -#ifdef WITH_SSH1 - if (session->version == 1) { - return SSH_AUTH_DENIED; - } -#endif - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Wrong state during pending SSH call"); - return SSH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* public key */ - rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_s); - if (rc < 0) { - goto fail; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "publickey", - 0, /* private key ? */ - pubkey->type_c, /* algo */ - pubkey_s /* public key */ - ); - if (rc < 0) { - goto fail; - } - - ssh_string_free(pubkey_s); - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_string_free(pubkey_s); - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -/** - * @brief Authenticate with public/private key. - * - * @param[in] session The SSH session. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] privkey The private key for authentication. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_publickey(ssh_session session, - const char *username, - const ssh_key privkey) -{ - ssh_string str = NULL; - int rc; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (privkey == NULL || !ssh_key_is_private(privkey)) { - ssh_set_error(session, SSH_FATAL, "Invalid private key"); - return SSH_AUTH_ERROR; - } - -#ifdef WITH_SSH1 - if (session->version == 1) { - return SSH_AUTH_DENIED; - } -#endif - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_PUBKEY: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Bad call during pending SSH call in ssh_userauth_try_pubkey"); - return SSH_AUTH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* public key */ - rc = ssh_pki_export_pubkey_blob(privkey, &str); - if (rc < 0) { - goto fail; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "publickey", - 1, /* private key */ - privkey->type_c, /* algo */ - str /* public key */ - ); - if (rc < 0) { - goto fail; - } - ssh_string_free(str); - - /* sign the buffer with the private key */ - str = ssh_pki_do_sign(session, session->out_buffer, privkey); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - str = NULL; - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_string_free(str); - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -#ifndef _WIN32 -static int ssh_userauth_agent_publickey(ssh_session session, - const char *username, - ssh_key pubkey) -{ - ssh_string str = NULL; - int rc; - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_AGENT: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Bad call during pending SSH call in ssh_userauth_try_pubkey"); - return SSH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - - /* public key */ - rc = ssh_pki_export_pubkey_blob(pubkey, &str); - if (rc < 0) { - goto fail; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "publickey", - 1, /* private key */ - pubkey->type_c, /* algo */ - str /* public key */ - ); - if (rc < 0) { - goto fail; - } - - ssh_string_free(str); - - /* sign the buffer with the private key */ - str = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_AGENT; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - ssh_string_free(str); - - return SSH_AUTH_ERROR; -} - -enum ssh_agent_state_e { - SSH_AGENT_STATE_NONE = 0, - SSH_AGENT_STATE_PUBKEY, - SSH_AGENT_STATE_AUTH -}; - -struct ssh_agent_state_struct { - enum ssh_agent_state_e state; - ssh_key pubkey; - char *comment; -}; - - -/** - * @brief Try to do public key authentication with ssh agent. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_agent(ssh_session session, - const char *username) { - int rc = SSH_AUTH_ERROR; - struct ssh_agent_state_struct *state; - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (!agent_is_running(session)) { - return SSH_AUTH_DENIED; - } - if (!session->agent_state){ - session->agent_state = malloc(sizeof(struct ssh_agent_state_struct)); - if (!session->agent_state){ - ssh_set_error_oom(session); - return SSH_AUTH_ERROR; - } - ZERO_STRUCTP(session->agent_state); - session->agent_state->state=SSH_AGENT_STATE_NONE; - } - state = session->agent_state; - if (state->pubkey == NULL) - state->pubkey = ssh_agent_get_first_ident(session, &state->comment); - while (state->pubkey != NULL) { - if(state->state == SSH_AGENT_STATE_NONE){ - SSH_LOG(SSH_LOG_DEBUG, - "Trying identity %s", state->comment); - } - if(state->state == SSH_AGENT_STATE_NONE || - state->state == SSH_AGENT_STATE_PUBKEY){ - rc = ssh_userauth_try_publickey(session, username, state->pubkey); - if (rc == SSH_AUTH_ERROR) { - ssh_string_free_char(state->comment); - ssh_key_free(state->pubkey); - SAFE_FREE(session->agent_state); - return rc; - } else if (rc == SSH_AUTH_AGAIN) { - state->state = SSH_AGENT_STATE_PUBKEY; - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_DEBUG, - "Public key of %s refused by server", state->comment); - ssh_string_free_char(state->comment); - ssh_key_free(state->pubkey); - state->pubkey = ssh_agent_get_next_ident(session, &state->comment); - state->state = SSH_AGENT_STATE_NONE; - continue; - } - - SSH_LOG(SSH_LOG_DEBUG, - "Public key of %s accepted by server", state->comment); - state->state = SSH_AGENT_STATE_AUTH; - } - if (state->state == SSH_AGENT_STATE_AUTH){ - rc = ssh_userauth_agent_publickey(session, username, state->pubkey); - if (rc == SSH_AUTH_AGAIN) - return rc; - ssh_string_free_char(state->comment); - ssh_key_free(state->pubkey); - if (rc == SSH_AUTH_ERROR) { - SAFE_FREE(session->agent_state); - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_INFO, - "Server accepted public key but refused the signature"); - state->pubkey = ssh_agent_get_next_ident(session, &state->comment); - state->state = SSH_AGENT_STATE_NONE; - continue; - } - SAFE_FREE(session->agent_state); - return SSH_AUTH_SUCCESS; - } - } - - SAFE_FREE(session->agent_state); - return rc; -} -#endif - -enum ssh_auth_auto_state_e { - SSH_AUTH_AUTO_STATE_NONE=0, - SSH_AUTH_AUTO_STATE_PUBKEY, - SSH_AUTH_AUTO_STATE_KEY_IMPORTED, - SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED -}; - -struct ssh_auth_auto_state_struct { - enum ssh_auth_auto_state_e state; - struct ssh_iterator *it; - ssh_key privkey; - ssh_key pubkey; -}; - -/** - * @brief Tries to automatically authenticate with public key and "none" - * - * It may fail, for instance it doesn't ask for a password and uses a default - * asker for passphrases (in case the private key is encrypted). - * - * @param[in] session The SSH session. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] passphrase Use this passphrase to unlock the privatekey. Use NULL - * if you don't want to use a passphrase or the user - * should be asked. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_publickey_auto(ssh_session session, - const char *username, - const char *passphrase) -{ - ssh_auth_callback auth_fn = NULL; - void *auth_data = NULL; - struct ssh_auth_auto_state_struct *state; - int rc; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (session->common.callbacks) { - auth_fn = session->common.callbacks->auth_function; - auth_data = session->common.callbacks->userdata; - } - if (!session->auth_auto_state){ - session->auth_auto_state = - malloc(sizeof(struct ssh_auth_auto_state_struct)); - if (!session->auth_auto_state){ - ssh_set_error_oom(session); - return SSH_AUTH_ERROR; - } - ZERO_STRUCTP(session->auth_auto_state); - } - state = session->auth_auto_state; - if (state->state == SSH_AUTH_AUTO_STATE_NONE) { -#ifndef _WIN32 - /* Try authentication with ssh-agent first */ - rc = ssh_userauth_agent(session, username); - if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_AGAIN) { - return rc; - } -#endif - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - } - if (state->it == NULL) { - state->it = ssh_list_get_iterator(session->opts.identity); - } - - while (state->it != NULL){ - const char *privkey_file = state->it->data; - char pubkey_file[1024] = {0}; - if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY){ - SSH_LOG(SSH_LOG_DEBUG, - "Trying to authenticate with %s", privkey_file); - state->privkey = NULL; - state->pubkey = NULL; - snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file); - - rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey); - if (rc == SSH_ERROR) { - ssh_set_error(session, - SSH_FATAL, - "Failed to import public key: %s", - pubkey_file); - SAFE_FREE(session->auth_auto_state); - return SSH_AUTH_ERROR; - } else if (rc == SSH_EOF) { - /* Read the private key and save the public key to file */ - rc = ssh_pki_import_privkey_file(privkey_file, - passphrase, - auth_fn, - auth_data, - &state->privkey); - if (rc == SSH_ERROR) { - ssh_set_error(session, - SSH_FATAL, - "Failed to read private key: %s", - privkey_file); - state->it=state->it->next; - continue; - } else if (rc == SSH_EOF) { - /* If the file doesn't exist, continue */ - SSH_LOG(SSH_LOG_DEBUG, - "Private key %s doesn't exist.", - privkey_file); - state->it=state->it->next; - continue; - } - - rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey); - if (rc == SSH_ERROR) { - ssh_key_free(state->privkey); - SAFE_FREE(session->auth_auto_state); - return SSH_AUTH_ERROR; - } - - rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file); - if (rc == SSH_ERROR) { - SSH_LOG(SSH_LOG_WARN, - "Could not write public key to file: %s", - pubkey_file); - } - } - state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED; - } - if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED){ - rc = ssh_userauth_try_publickey(session, username, state->pubkey); - if (rc == SSH_AUTH_ERROR) { - SSH_LOG(SSH_LOG_WARN, - "Public key authentication error for %s", - privkey_file); - ssh_key_free(state->privkey); - ssh_key_free(state->pubkey); - SAFE_FREE(session->auth_auto_state); - return rc; - } else if (rc == SSH_AUTH_AGAIN){ - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_DEBUG, - "Public key for %s refused by server", - privkey_file); - ssh_key_free(state->privkey); - state->privkey = NULL; - ssh_key_free(state->pubkey); - state->pubkey = NULL; - state->it=state->it->next; - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - continue; - } - state->state = SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED; - } - if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED){ - /* Public key has been accepted by the server */ - if (state->privkey == NULL) { - rc = ssh_pki_import_privkey_file(privkey_file, - passphrase, - auth_fn, - auth_data, - &state->privkey); - if (rc == SSH_ERROR) { - ssh_key_free(state->pubkey); - state->pubkey=NULL; - ssh_set_error(session, - SSH_FATAL, - "Failed to read private key: %s", - privkey_file); - state->it=state->it->next; - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - continue; - } else if (rc == SSH_EOF) { - /* If the file doesn't exist, continue */ - ssh_key_free(state->pubkey); - state->pubkey=NULL; - SSH_LOG(SSH_LOG_INFO, - "Private key %s doesn't exist.", - privkey_file); - state->it=state->it->next; - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - continue; - } - } - - rc = ssh_userauth_publickey(session, username, state->privkey); - if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) { - ssh_key_free(state->privkey); - ssh_key_free(state->pubkey); - SAFE_FREE(session->auth_auto_state); - } - if (rc == SSH_AUTH_ERROR) { - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_INFO, - "Successfully authenticated using %s", - privkey_file); - return rc; - } else if (rc == SSH_AUTH_AGAIN){ - return rc; - } - - SSH_LOG(SSH_LOG_WARN, - "The server accepted the public key but refused the signature"); - state->it=state->it->next; - state->state=SSH_AUTH_AUTO_STATE_PUBKEY; - /* continue */ - } - } - SSH_LOG(SSH_LOG_INFO, - "Tried every public key, none matched"); - SAFE_FREE(session->auth_auto_state); - return SSH_AUTH_DENIED; -} - -/** - * @brief Try to authenticate by password. - * - * This authentication method is normally disabled on SSHv2 server. You should - * use keyboard-interactive mode. - * - * The 'password' value MUST be encoded UTF-8. It is up to the server how to - * interpret the password and validate it against the password database. - * However, if you read the password in some other encodi |