From ff5bf51af1247cdcb6ffda4f940a24536340a9a9 Mon Sep 17 00:00:00 2001 From: Aris Adamantiadis Date: Sun, 11 Sep 2011 01:56:30 +0200 Subject: Auth: nonblocking ssh_userauth_pubkey_auto --- include/libssh/session.h | 1 + src/auth.c | 271 ++++++++++++++++++++++++++++------------------- 2 files changed, 162 insertions(+), 110 deletions(-) diff --git a/include/libssh/session.h b/include/libssh/session.h index b2d610de..894b8453 100644 --- a/include/libssh/session.h +++ b/include/libssh/session.h @@ -118,6 +118,7 @@ struct ssh_session_struct { enum ssh_auth_state_e auth_state; enum ssh_channel_request_state_e global_req_state; struct ssh_agent_state_struct *agent_state; + struct ssh_auth_auto_state_struct *auth_auto_state; KEX server_kex; KEX client_kex; diff --git a/src/auth.c b/src/auth.c index 84cb92e4..a683e455 100644 --- a/src/auth.c +++ b/src/auth.c @@ -1010,6 +1010,20 @@ int ssh_userauth_agent(ssh_session session, } #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" * @@ -1043,9 +1057,9 @@ int ssh_userauth_publickey_auto(ssh_session session, const char *username, const char *passphrase) { - struct ssh_iterator *it; ssh_auth_callback auth_fn = NULL; void *auth_data = NULL; + struct ssh_auth_auto_state_struct *state; int rc; if (session == NULL) { @@ -1056,141 +1070,178 @@ int ssh_userauth_publickey_auto(ssh_session session, 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_ERROR || rc == SSH_AUTH_SUCCESS) { - return rc; - } + rc = ssh_userauth_agent(session, username); + if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_SUCCESS) { + return rc; + } + if (rc == SSH_AUTH_AGAIN) + return rc; #endif - - for (it = ssh_list_get_iterator(session->identity); - it != NULL; - it = it->next) { - const char *privkey_file = it->data; + state->state = SSH_AUTH_AUTO_STATE_PUBKEY; + } + if (state->it == NULL) + state->it = ssh_list_get_iterator(session->identity); + while (state->it != NULL){ + const char *privkey_file = state->it->data; char pubkey_file[1024] = {0}; - ssh_key privkey = NULL; - ssh_key pubkey; - - ssh_log(session, - SSH_LOG_PROTOCOL, - "Trying to authenticate with %s", - privkey_file); - - snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file); + if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY){ + ssh_log(session, + SSH_LOG_PROTOCOL, + "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, &pubkey); - if (rc == SSH_ERROR) { - ssh_set_error(session, - SSH_FATAL, - "Failed to import public key: %s", - pubkey_file); - 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, - &privkey); + rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey); if (rc == SSH_ERROR) { ssh_set_error(session, - SSH_FATAL, - "Failed to read private key: %s", - privkey_file); - continue; + 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) { - /* If the file doesn't exist, continue */ + /* 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(session, + SSH_LOG_PACKET, + "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(session, + SSH_LOG_PACKET, + "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(session, + SSH_LOG_RARE, + "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(session, - SSH_LOG_PACKET, - "Private key %s doesn't exist.", + SSH_LOG_PROTOCOL, + "Public key for %s refused by server", privkey_file); + ssh_key_free(state->privkey); + ssh_key_free(state->pubkey); + state->it=state->it->next; continue; } - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - if (rc == SSH_ERROR) { - ssh_key_free(privkey); - return SSH_AUTH_ERROR; + 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(session, + SSH_LOG_PACKET, + "Private key %s doesn't exist.", + privkey_file); + state->it=state->it->next; + state->state = SSH_AUTH_AUTO_STATE_PUBKEY; + continue; + } } - rc = ssh_pki_export_pubkey_file(pubkey, pubkey_file); - if (rc == SSH_ERROR) { - ssh_log(session, - SSH_LOG_PACKET, - "Could not write public key to file: %s", - pubkey_file); + 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); } - } - - rc = ssh_userauth_try_publickey(session, username, pubkey); - if (rc == SSH_AUTH_ERROR) { - ssh_log(session, - SSH_LOG_RARE, - "Public key authentication error for %s", - privkey_file); - ssh_key_free(privkey); - ssh_key_free(pubkey); - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - ssh_log(session, - SSH_LOG_PROTOCOL, - "Public key for %s refused by server", - privkey_file); - ssh_key_free(privkey); - ssh_key_free(pubkey); - continue; - } - - /* Public key has been accepted by the server */ - if (privkey == NULL) { - rc = ssh_pki_import_privkey_file(privkey_file, - passphrase, - auth_fn, - auth_data, - &privkey); - if (rc == SSH_ERROR) { - ssh_key_free(pubkey); - ssh_set_error(session, - SSH_FATAL, - "Failed to read private key: %s", - privkey_file); - continue; - } else if (rc == SSH_EOF) { - /* If the file doesn't exist, continue */ - ssh_key_free(pubkey); + if (rc == SSH_AUTH_ERROR) { + return rc; + } else if (rc == SSH_AUTH_SUCCESS) { ssh_log(session, - SSH_LOG_PACKET, - "Private key %s doesn't exist.", + SSH_LOG_PROTOCOL, + "Successfully authenticated using %s", privkey_file); - continue; + return rc; + } else if (rc == SSH_AUTH_AGAIN){ + return rc; } - } - rc = ssh_userauth_publickey(session, username, privkey); - ssh_key_free(privkey); - ssh_key_free(pubkey); - if (rc == SSH_AUTH_ERROR) { - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { ssh_log(session, - SSH_LOG_PROTOCOL, - "Successfully authenticated using %s", - privkey_file); - return rc; + SSH_LOG_RARE, + "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(session, - SSH_LOG_RARE, - "The server accepted the public key but refused the signature"); - /* continue */ } ssh_log(session, SSH_LOG_PROTOCOL, "Tried every public key, none matched"); - + SAFE_FREE(session->auth_auto_state); return SSH_AUTH_DENIED; } -- cgit v1.2.3