aboutsummaryrefslogtreecommitdiff
path: root/src/auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/auth.c')
-rw-r--r--src/auth.c676
1 files changed, 530 insertions, 146 deletions
diff --git a/src/auth.c b/src/auth.c
index 89272290..0acd9686 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -47,7 +47,7 @@
#include "libssh/legacy.h"
/**
- * @defgroup libssh_auth The SSH authentication functions.
+ * @defgroup libssh_auth The SSH authentication functions
* @ingroup libssh
*
* Functions to authenticate with a server.
@@ -58,7 +58,7 @@
/**
* @internal
*
- * @brief Ask access to the ssh-userauth service.
+ * @brief Ask for access to the ssh-userauth service.
*
* @param[in] session The SSH session handle.
*
@@ -66,19 +66,21 @@
* @returns SSH_AGAIN on nonblocking mode, if calling that function
* again is necessary
*/
-static int ssh_userauth_request_service(ssh_session session) {
+static int ssh_userauth_request_service(ssh_session session)
+{
int rc;
rc = ssh_service_request(session, "ssh-userauth");
if ((rc != SSH_OK) && (rc != SSH_AGAIN)) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to request \"ssh-userauth\" service");
}
return rc;
}
-static int ssh_auth_response_termination(void *user) {
+static int ssh_auth_response_termination(void *user)
+{
ssh_session session = (ssh_session)user;
switch (session->auth.state) {
case SSH_AUTH_STATE_NONE:
@@ -139,7 +141,8 @@ static const char *ssh_auth_get_current_method(ssh_session session)
* 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) {
+static int ssh_userauth_get_response(ssh_session session)
+{
int rc = SSH_AUTH_ERROR;
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
@@ -199,7 +202,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_banner) {
banner = ssh_buffer_get_ssh_string(packet);
if (banner == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid SSH_USERAUTH_BANNER packet");
} else {
SSH_LOG(SSH_LOG_DEBUG,
@@ -237,7 +240,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
if (partial) {
session->auth.state = SSH_AUTH_STATE_PARTIAL;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Partial success for '%s'. Authentication that can continue: %s",
current_method,
auth_methods);
@@ -247,7 +250,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
"Access denied for '%s'. Authentication that can continue: %s",
current_method,
auth_methods);
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"%s",
ssh_get_error(session));
@@ -364,7 +367,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok) {
*
* @param[in] username Deprecated, set to NULL.
*
- * @returns A bitfield of the fllowing values:
+ * @returns A bitfield of the following values:
* - SSH_AUTH_METHOD_PASSWORD
* - SSH_AUTH_METHOD_PUBLICKEY
* - SSH_AUTH_METHOD_HOSTBASED
@@ -403,10 +406,11 @@ int ssh_userauth_list(ssh_session session, const char *username)
* 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 ssh_userauth_none(ssh_session session, const char *username)
+{
int rc;
- switch(session->pending_call_state) {
+ switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_NONE:
@@ -492,6 +496,7 @@ int ssh_userauth_try_publickey(ssh_session session,
{
ssh_string pubkey_s = NULL;
const char *sig_type_c = NULL;
+ bool allowed;
int rc;
if (session == NULL) {
@@ -513,7 +518,7 @@ int ssh_userauth_try_publickey(ssh_session session,
SSH_FATAL,
"Wrong state (%d) during pending SSH call",
session->pending_call_state);
- return SSH_ERROR;
+ return SSH_AUTH_ERROR;
}
/* Check if the given public key algorithm is allowed */
@@ -523,13 +528,21 @@ int ssh_userauth_try_publickey(ssh_session session,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, pubkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ return SSH_AUTH_DENIED;
+ }
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@@ -544,6 +557,7 @@ int ssh_userauth_try_publickey(ssh_session session,
goto fail;
}
+ SSH_LOG(SSH_LOG_TRACE, "Trying signature type %s", sig_type_c);
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
SSH2_MSG_USERAUTH_REQUEST,
@@ -611,6 +625,7 @@ int ssh_userauth_publickey(ssh_session session,
const ssh_key privkey)
{
ssh_string str = NULL;
+ bool allowed;
int rc;
const char *sig_type_c = NULL;
enum ssh_keytypes_e key_type;
@@ -647,13 +662,21 @@ int ssh_userauth_publickey(ssh_session session,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, privkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(privkey));
+ return SSH_AUTH_DENIED;
+ }
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@@ -668,6 +691,7 @@ int ssh_userauth_publickey(ssh_session session,
goto fail;
}
+ SSH_LOG(SSH_LOG_TRACE, "Sending signature type %s", sig_type_c);
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
SSH2_MSG_USERAUTH_REQUEST,
@@ -722,7 +746,6 @@ fail:
return SSH_AUTH_ERROR;
}
-#ifndef _WIN32
static int ssh_userauth_agent_publickey(ssh_session session,
const char *username,
ssh_key pubkey)
@@ -730,18 +753,20 @@ static int ssh_userauth_agent_publickey(ssh_session session,
ssh_string pubkey_s = NULL;
ssh_string sig_blob = NULL;
const char *sig_type_c = NULL;
+ bool allowed;
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_publickey");
- return SSH_ERROR;
+ 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 %s",
+ __func__);
+ return SSH_ERROR;
}
rc = ssh_userauth_request_service(session);
@@ -765,7 +790,8 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
@@ -773,17 +799,25 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, pubkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ SSH_STRING_FREE(pubkey_s);
+ return SSH_AUTH_DENIED;
+ }
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
- SSH2_MSG_USERAUTH_REQUEST,
- username ? username : session->opts.username,
- "ssh-connection",
- "publickey",
- 1, /* private key */
- sig_type_c, /* algo */
- pubkey_s /* public key */
- );
+ SSH2_MSG_USERAUTH_REQUEST,
+ username ? username : session->opts.username,
+ "ssh-connection",
+ "publickey",
+ 1, /* private key */
+ sig_type_c, /* algo */
+ pubkey_s /* public key */
+ );
SSH_STRING_FREE(pubkey_s);
if (rc < 0) {
goto fail;
@@ -827,6 +861,7 @@ fail:
enum ssh_agent_state_e {
SSH_AGENT_STATE_NONE = 0,
SSH_AGENT_STATE_PUBKEY,
+ SSH_AGENT_STATE_CERT,
SSH_AGENT_STATE_AUTH
};
@@ -837,7 +872,8 @@ struct ssh_agent_state_struct {
};
/* Internal function */
-void ssh_agent_state_free(void *data) {
+void ssh_agent_state_free(void *data)
+{
struct ssh_agent_state_struct *state = data;
if (state) {
@@ -870,9 +906,15 @@ void ssh_agent_state_free(void *data) {
* before you connect to the server.
*/
int ssh_userauth_agent(ssh_session session,
- const char *username) {
+ const char *username)
+{
int rc = SSH_AUTH_ERROR;
- struct ssh_agent_state_struct *state;
+ struct ssh_agent_state_struct *state = NULL;
+ ssh_key *configKeys = NULL;
+ ssh_key *configCerts = NULL;
+ size_t configKeysCount = 0;
+ size_t configCertsCount = 0;
+ size_t i;
if (session == NULL) {
return SSH_AUTH_ERROR;
@@ -889,7 +931,7 @@ int ssh_userauth_agent(ssh_session session,
return SSH_AUTH_ERROR;
}
ZERO_STRUCTP(session->agent_state);
- session->agent_state->state=SSH_AGENT_STATE_NONE;
+ session->agent_state->state = SSH_AGENT_STATE_NONE;
}
state = session->agent_state;
@@ -901,70 +943,239 @@ int ssh_userauth_agent(ssh_session session,
return SSH_AUTH_DENIED;
}
+ if (session->opts.identities_only) {
+ /*
+ * Read keys mentioned in the config, so we can check if key from agent
+ * is in there.
+ */
+ size_t identityLen = ssh_list_count(session->opts.identity);
+ size_t certsLen = ssh_list_count(session->opts.certificate);
+ struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
+
+ configKeys = malloc(identityLen * sizeof(ssh_key));
+ configCerts = malloc((certsLen + identityLen) * sizeof(ssh_key));
+ if (configKeys == NULL || configCerts == NULL) {
+ free(configKeys);
+ free(configCerts);
+ ssh_set_error_oom(session);
+ return SSH_AUTH_ERROR;
+ }
+
+ while (it != NULL && configKeysCount < identityLen) {
+ const char *privkeyFile = it->data;
+ size_t certPathLen;
+ char *certFile = NULL;
+ ssh_key pubkey = NULL;
+ ssh_key cert = NULL;
+
+ /*
+ * Read the private key file listed in the config, but we're only
+ * interested in the public key. Don't try to decrypt private key.
+ */
+ rc = ssh_pki_import_pubkey_file(privkeyFile, &pubkey);
+ if (rc == SSH_OK) {
+ configKeys[configKeysCount++] = pubkey;
+ } else {
+ char *pubkeyFile = NULL;
+ size_t pubkeyPathLen = strlen(privkeyFile) + sizeof(".pub");
+
+ SSH_KEY_FREE(pubkey);
+
+ /*
+ * If we couldn't get the public key from the private key file,
+ * try a .pub file instead.
+ */
+ pubkeyFile = malloc(pubkeyPathLen);
+ if (!pubkeyFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(pubkeyFile, pubkeyPathLen, "%s.pub", privkeyFile);
+ rc = ssh_pki_import_pubkey_file(pubkeyFile, &pubkey);
+ free(pubkeyFile);
+ if (rc == SSH_OK) {
+ configKeys[configKeysCount++] = pubkey;
+ } else if (pubkey) {
+ SSH_KEY_FREE(pubkey);
+ }
+ }
+ /* Now try to see if there is a certificate with default name
+ * do not merge it yet with the key as we need to try first the
+ * non-certified key */
+ certPathLen = strlen(privkeyFile) + sizeof("-cert.pub");
+ certFile = malloc(certPathLen);
+ if (!certFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(certFile, certPathLen, "%s-cert.pub", privkeyFile);
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ free(certFile);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ /* And now load separately-listed certificates. */
+ it = ssh_list_get_iterator(session->opts.certificate);
+ while (it != NULL && configCertsCount < certsLen + identityLen) {
+ const char *certFile = it->data;
+ ssh_key cert = NULL;
+
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ }
+
while (state->pubkey != NULL) {
if (state->state == SSH_AGENT_STATE_NONE) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying identity %s", state->comment);
+ "Trying identity %s",
+ state->comment);
+ if (session->opts.identities_only) {
+ /* Check if this key is one of the keys listed in the config */
+ bool found_key = false;
+ for (i = 0; i < configKeysCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configKeys[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+ /* or in separate certificates */
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+
+ if (!found_key) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Identities only is enabled and identity %s was "
+ "not listed in config, skipping",
+ state->comment);
+ SSH_STRING_FREE_CHAR(state->comment);
+ state->comment = NULL;
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_agent_get_next_ident(
+ session, &state->comment);
+
+ if (state->pubkey == NULL) {
+ rc = SSH_AUTH_DENIED;
+ }
+ continue;
+ }
+ }
}
if (state->state == SSH_AGENT_STATE_NONE ||
- state->state == SSH_AGENT_STATE_PUBKEY) {
+ state->state == SSH_AGENT_STATE_PUBKEY ||
+ state->state == SSH_AGENT_STATE_CERT) {
rc = ssh_userauth_try_publickey(session, username, state->pubkey);
if (rc == SSH_AUTH_ERROR) {
- ssh_agent_state_free (state);
+ ssh_agent_state_free(state);
session->agent_state = NULL;
- return rc;
+ goto done;
} else if (rc == SSH_AUTH_AGAIN) {
- state->state = SSH_AGENT_STATE_PUBKEY;
- return rc;
+ state->state = (state->state == SSH_AGENT_STATE_NONE ?
+ SSH_AGENT_STATE_PUBKEY : state->state);
+ goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s refused by server", state->comment);
+ "Public key of %s refused by server",
+ state->comment);
+ if (state->state == SSH_AGENT_STATE_PUBKEY) {
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Retry with matching certificate");
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_key_dup(configCerts[i]);
+ state->state = SSH_AGENT_STATE_CERT;
+ continue;
+ }
+ }
+ }
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &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);
+ "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;
+ if (rc == SSH_AUTH_AGAIN) {
+ goto done;
+ }
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_PARTIAL) {
- ssh_agent_state_free (session->agent_state);
+ ssh_agent_state_free(session->agent_state);
session->agent_state = NULL;
- return rc;
+ goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Server accepted public key but refused the signature");
- ssh_key_free(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &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_agent_state_free (session->agent_state);
session->agent_state = NULL;
- return SSH_AUTH_SUCCESS;
+ rc = SSH_AUTH_SUCCESS;
+ goto done;
}
}
ssh_agent_state_free (session->agent_state);
session->agent_state = NULL;
+done:
+ for (i = 0; i < configKeysCount; i++) {
+ ssh_key_free(configKeys[i]);
+ }
+ free(configKeys);
+ for (i = 0; i < configCertsCount; i++) {
+ ssh_key_free(configCerts[i]);
+ }
+ free(configCerts);
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_CERTIFICATE_FILE,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION,
SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED
};
@@ -973,9 +1184,61 @@ struct ssh_auth_auto_state_struct {
struct ssh_iterator *it;
ssh_key privkey;
ssh_key pubkey;
+ ssh_key cert;
+ struct ssh_iterator *cert_it;
};
/**
+ * @brief Get the identity that is currently being processed by
+ * ssh_userauth_publickey_auto()
+ *
+ * This is meant to be used by a callback that happens as part of the
+ * execution of ssh_userauth_publickey_auto(). The auth_function
+ * callback might want to know which key a passphrase is needed for,
+ * for example.
+ *
+ * @param[in] session The SSH session.
+ *
+ * @param[out] value The value to get into. As a char**, space will be
+ * allocated by the function for the value, it is
+ * your responsibility to free the memory using
+ * ssh_string_free_char().
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
+ char** value)
+{
+ const char *id = NULL;
+
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return SSH_ERROR;
+ }
+
+ if (session->auth.auto_state != NULL &&
+ session->auth.auto_state->it != NULL) {
+ id = session->auth.auto_state->it->data;
+ }
+
+ if (id == NULL) {
+ return SSH_ERROR;
+ }
+
+ *value = strdup(id);
+ if (*value == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+/**
* @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
@@ -995,14 +1258,16 @@ struct ssh_auth_auto_state_struct {
* 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_publickey().\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.
+ *
+ * The OpenSSH iterates over the identities and first try the plain public key
+ * and then the certificate if it is in place.
*/
int ssh_userauth_publickey_auto(ssh_session session,
const char *username,
@@ -1010,7 +1275,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
{
ssh_auth_callback auth_fn = NULL;
void *auth_data = NULL;
- struct ssh_auth_auto_state_struct *state;
+ struct ssh_auth_auto_state_struct *state = NULL;
int rc;
if (session == NULL) {
@@ -1037,15 +1302,13 @@ int ssh_userauth_publickey_auto(ssh_session session,
}
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_PARTIAL ||
- rc == SSH_AUTH_AGAIN ) {
+ rc == SSH_AUTH_AGAIN) {
return rc;
}
-#endif
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
}
if (state->it == NULL) {
@@ -1054,14 +1317,17 @@ int ssh_userauth_publickey_auto(ssh_session session,
while (state->it != NULL) {
const char *privkey_file = state->it->data;
- char pubkey_file[1024] = {0};
+ char pubkey_file[PATH_MAX] = {0};
if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying to authenticate with %s", privkey_file);
+ "Trying to authenticate with %s",
+ privkey_file);
+ state->cert = NULL;
state->privkey = NULL;
state->pubkey = NULL;
+#ifdef WITH_PKCS11_URI
if (ssh_pki_is_uri(privkey_file)) {
char *pub_uri_from_priv = NULL;
SSH_LOG(SSH_LOG_INFO,
@@ -1070,84 +1336,165 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (pub_uri_from_priv == NULL) {
return SSH_ERROR;
} else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s",
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s",
pub_uri_from_priv);
SAFE_FREE(pub_uri_from_priv);
}
- } else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file);
+ } else
+#endif /* WITH_PKCS11_URI */
+ {
+ 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);
+ 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);
+ 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;
+ 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;
+ state->it = state->it->next;
continue;
}
- rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey);
+ rc = ssh_pki_export_privkey_to_pubkey(state->privkey,
+ &state->pubkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->privkey);
+ 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,
+ SSH_LOG(SSH_LOG_TRACE,
"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 (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ ssh_key k = state->pubkey;
+ if (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ k = state->cert;
+ }
+ rc = ssh_userauth_try_publickey(session, username, k);
if (rc == SSH_AUTH_ERROR) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Public key authentication error for %s",
privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
+ SSH_KEY_FREE(state->cert);
+ 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) {
+ int r; /* do not reuse `rc` as it is used to return from here */
+ SSH_KEY_FREE(state->cert);
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;
+ "Public key for %s%s refused by server",
+ privkey_file,
+ (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED
+ ? " (with certificate)" : ""));
+ /* Try certificate file by appending -cert.pub (if present) */
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ char cert_file[PATH_MAX] = {0};
+ ssh_key cert = NULL;
+
+ snprintf(cert_file,
+ sizeof(cert_file),
+ "%s-cert.pub",
+ privkey_file);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (default path)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ /* TODO check the pubkey and certs match */
+ SSH_LOG(SSH_LOG_TRACE,
+ "Certificate loaded %s. Retry the authentication.",
+ cert_file);
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE;
+ SSH_KEY_FREE(state->cert);
+ state->cert = cert;
+ /* try to authenticate with this certificate */
+ continue;
+ }
+ /* if the file does not exists, try configuration options */
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ }
+ /* Try certificate files loaded through options */
+ if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ SSH_KEY_FREE(state->cert);
+ if (state->cert_it == NULL) {
+ state->cert_it = ssh_list_get_iterator(session->opts.certificate);
+ }
+ while (state->cert_it != NULL) {
+ const char *cert_file = state->cert_it->data;
+ ssh_key cert = NULL;
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (options)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ int cmp = ssh_key_cmp(cert,
+ state->pubkey,
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp != 0) {
+ state->cert_it = state->cert_it->next;
+ SSH_KEY_FREE(cert);
+ continue; /* with next cert */
+ }
+ SSH_LOG(SSH_LOG_TRACE,
+ "Found matching certificate %s in options. Retry the authentication.",
+ cert_file);
+ state->cert = cert;
+ cert = NULL;
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ /* try to authenticate with this identity */
+ break; /* try this cert */
+ }
+ /* continue with next identity */
+ }
+ if (state->cert != NULL) {
+ continue; /* retry with the certificate */
+ }
+ }
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
}
@@ -1157,25 +1504,25 @@ int ssh_userauth_publickey_auto(ssh_session session,
/* 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);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->pubkey);
- state->pubkey=NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ 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,
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
+ SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
state->it = state->it->next;
@@ -1183,16 +1530,33 @@ int ssh_userauth_publickey_auto(ssh_session session,
continue;
}
}
+ if (state->cert != NULL && !is_cert_type(state->privkey->cert_type)) {
+ rc = ssh_pki_copy_cert_to_privkey(state->cert, state->privkey);
+ if (rc != SSH_OK) {
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to copy cert to private key");
+ 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);
+ bool cert_used = (state->cert != NULL);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
if (rc == SSH_AUTH_SUCCESS) {
- SSH_LOG(SSH_LOG_INFO,
- "Successfully authenticated using %s",
- privkey_file);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Successfully authenticated using %s%s",
+ privkey_file,
+ (cert_used ? " and certificate" : ""));
}
return rc;
}
@@ -1200,18 +1564,19 @@ int ssh_userauth_publickey_auto(ssh_session session,
return rc;
}
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_DEBUG,
"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");
+ SSH_LOG(SSH_LOG_WARN,
+ "Access denied: Tried every public key, none matched");
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_DENIED;
}
@@ -1250,10 +1615,11 @@ int ssh_userauth_publickey_auto(ssh_session session,
*/
int ssh_userauth_password(ssh_session session,
const char *username,
- const char *password) {
+ const char *password)
+{
int rc;
- switch(session->pending_call_state) {
+ switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_PASSWORD:
@@ -1311,7 +1677,6 @@ fail:
return SSH_AUTH_ERROR;
}
-#ifndef _WIN32
/* LEGACY */
int ssh_userauth_agent_pubkey(ssh_session session,
const char *username,
@@ -1328,20 +1693,26 @@ int ssh_userauth_agent_pubkey(ssh_session session,
key->type = publickey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PUBLIC;
- key->dsa = publickey->dsa_pub;
+#ifndef HAVE_LIBCRYPTO
key->rsa = publickey->rsa_pub;
+#else
+ key->key = publickey->key_pub;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_userauth_agent_publickey(session, username, key);
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return rc;
}
-#endif /* _WIN32 */
-ssh_kbdint ssh_kbdint_new(void) {
+ssh_kbdint ssh_kbdint_new(void)
+{
ssh_kbdint kbd;
kbd = calloc(1, sizeof(struct ssh_kbdint_struct));
@@ -1353,7 +1724,8 @@ ssh_kbdint ssh_kbdint_new(void) {
}
-void ssh_kbdint_free(ssh_kbdint kbd) {
+void ssh_kbdint_free(ssh_kbdint kbd)
+{
size_t i, n;
if (kbd == NULL) {
@@ -1389,7 +1761,8 @@ void ssh_kbdint_free(ssh_kbdint kbd) {
SAFE_FREE(kbd);
}
-void ssh_kbdint_clean(ssh_kbdint kbd) {
+void ssh_kbdint_clean(ssh_kbdint kbd)
+{
size_t i, n;
if (kbd == NULL) {
@@ -1589,10 +1962,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
}
SSH_LOG(SSH_LOG_DEBUG,
- "%d keyboard-interactive prompts", nprompts);
+ "%" PRIu32 " keyboard-interactive prompts", nprompts);
if (nprompts > KBDINT_MAX_PROMPT) {
ssh_set_error(session, SSH_FATAL,
- "Too much prompts requested by the server: %u (0x%.4x)",
+ "Too much prompts requested by the server: %" PRIu32 " (0x%.4" PRIx32 ")",
nprompts, nprompts);
ssh_kbdint_free(session->kbdint);
session->kbdint = NULL;
@@ -1667,7 +2040,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
* @see ssh_userauth_kbdint_setanswer()
*/
int ssh_userauth_kbdint(ssh_session session, const char *user,
- const char *submethods) {
+ const char *submethods)
+{
int rc = SSH_AUTH_ERROR;
if (session == NULL) {
@@ -1709,7 +2083,8 @@ int ssh_userauth_kbdint(ssh_session session, const char *user,
*
* @returns The number of prompts.
*/
-int ssh_userauth_kbdint_getnprompts(ssh_session session) {
+int ssh_userauth_kbdint_getnprompts(ssh_session session)
+{
if (session == NULL) {
return SSH_ERROR;
}
@@ -1731,7 +2106,8 @@ int ssh_userauth_kbdint_getnprompts(ssh_session session) {
*
* @returns The name of the message block. Do not free it.
*/
-const char *ssh_userauth_kbdint_getname(ssh_session session) {
+const char *ssh_userauth_kbdint_getname(ssh_session session)
+{
if (session == NULL) {
return NULL;
}
@@ -1754,7 +2130,8 @@ const char *ssh_userauth_kbdint_getname(ssh_session session) {
* @returns The instruction of the message block.
*/
-const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
+const char *ssh_userauth_kbdint_getinstruction(ssh_session session)
+{
if (session == NULL)
return NULL;
if (session->kbdint == NULL) {
@@ -1789,8 +2166,9 @@ const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
* if (echo) ...
* @endcode
*/
-const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
- char *echo) {
+const char *
+ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo)
+{
if (session == NULL)
return NULL;
if (session->kbdint == NULL) {
@@ -1817,7 +2195,8 @@ const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
*
* @returns The number of answers.
*/
-int ssh_userauth_kbdint_getnanswers(ssh_session session) {
+int ssh_userauth_kbdint_getnanswers(ssh_session session)
+{
if (session == NULL || session->kbdint == NULL) {
return SSH_ERROR;
}
@@ -1825,15 +2204,17 @@ int ssh_userauth_kbdint_getnanswers(ssh_session session) {
}
/**
- * @brief Get the answer for a question from a message block.
+ * @brief Get the answer to a question from a message block.
*
* @param[in] session The ssh session to use.
*
* @param[in] i index The number of the ith answer.
*
- * @return 0 on success, < 0 on error.
+ * @return The answer string, or NULL if the answer is not
+ * available. Do not free the string.
*/
-const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
+const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i)
+{
if (session == NULL || session->kbdint == NULL
|| session->kbdint->answers == NULL) {
return NULL;
@@ -1864,8 +2245,10 @@ const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
*
* @return 0 on success, < 0 on error.
*/
-int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
- const char *answer) {
+int
+ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
+ const char *answer)
+{
if (session == NULL) {
return -1;
}
@@ -1911,7 +2294,8 @@ int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
* SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again
* later.
*/
-int ssh_userauth_gssapi(ssh_session session) {
+int ssh_userauth_gssapi(ssh_session session)
+{
int rc = SSH_AUTH_DENIED;
#ifdef WITH_GSSAPI
switch(session->pending_call_state) {
@@ -1933,7 +2317,7 @@ int ssh_userauth_gssapi(ssh_session session) {
} else if (rc == SSH_ERROR) {
return SSH_AUTH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi-with-mic");
+ SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-with-mic");
session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_MIC;
session->auth.state = SSH_AUTH_STATE_NONE;