From c221db0f3659d188911d5c0ce03d1378c4bc51c7 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Tue, 23 Aug 2011 21:28:14 +0200 Subject: [PATCH] auth: Add ssh_userauth_agent(). This commit is pretty big cause several functions have been refactored. --- include/libssh/agent.h | 10 +- include/libssh/legacy.h | 4 + include/libssh/libssh.h | 6 +- src/agent.c | 100 ++++---- src/auth.c | 545 +++++++++++++++------------------------- 5 files changed, 265 insertions(+), 400 deletions(-) diff --git a/include/libssh/agent.h b/include/libssh/agent.h index 2a0f229f..16dc7146 100644 --- a/include/libssh/agent.h +++ b/include/libssh/agent.h @@ -80,13 +80,13 @@ void agent_free(struct ssh_agent_struct *agent); */ int agent_is_running(struct ssh_session_struct *session); -int agent_get_ident_count(struct ssh_session_struct *session); +int ssh_agent_get_ident_count(struct ssh_session_struct *session); -struct ssh_public_key_struct *agent_get_next_ident(struct ssh_session_struct *session, - char **comment); +ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, + char **comment); -struct ssh_public_key_struct *agent_get_first_ident(struct ssh_session_struct *session, - char **comment); +ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session, + char **comment); ssh_string agent_sign_data(struct ssh_session_struct *session, struct ssh_buffer_struct *data, diff --git a/include/libssh/legacy.h b/include/libssh/legacy.h index 9e5c9f0f..e951c922 100644 --- a/include/libssh/legacy.h +++ b/include/libssh/legacy.h @@ -33,6 +33,10 @@ LIBSSH_API int ssh_auth_list(ssh_session session); LIBSSH_API int ssh_userauth_offer_pubkey(ssh_session session, const char *username, int type, ssh_string publickey); LIBSSH_API int ssh_userauth_pubkey(ssh_session session, const char *username, ssh_string publickey, ssh_private_key privatekey); +#ifndef _WIN32 +LIBSSH_API int ssh_userauth_agent_pubkey(ssh_session session, const char *username, + ssh_public_key publickey); +#endif LIBSSH_API void buffer_free(ssh_buffer buffer); LIBSSH_API void *buffer_get(ssh_buffer buffer); diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index acf1292d..0f3fcb62 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -500,11 +500,11 @@ LIBSSH_API int ssh_userauth_try_publickey(ssh_session session, LIBSSH_API int ssh_userauth_publickey(ssh_session session, const char *username, const ssh_key privkey); - #ifndef _WIN32 -LIBSSH_API int ssh_userauth_agent_pubkey(ssh_session session, const char *username, - ssh_public_key publickey); +LIBSSH_API int ssh_userauth_agent(ssh_session session, + const char *username); #endif + LIBSSH_API int ssh_userauth_autopubkey(ssh_session session, const char *passphrase); LIBSSH_API int ssh_userauth_kbdint(ssh_session session, const char *user, const char *submethods); LIBSSH_API const char *ssh_userauth_kbdint_getinstruction(ssh_session session); diff --git a/src/agent.c b/src/agent.c index 9566bdb0..ca448bdd 100644 --- a/src/agent.c +++ b/src/agent.c @@ -264,7 +264,7 @@ static int agent_talk(struct ssh_session_struct *session, return 0; } -int agent_get_ident_count(struct ssh_session_struct *session) { +int ssh_agent_get_ident_count(struct ssh_session_struct *session) { ssh_buffer request = NULL; ssh_buffer reply = NULL; unsigned int type = 0; @@ -337,71 +337,67 @@ int agent_get_ident_count(struct ssh_session_struct *session) { } /* caller has to free commment */ -struct ssh_public_key_struct *agent_get_first_ident(struct ssh_session_struct *session, - char **comment) { - if (agent_get_ident_count(session) > 0) { - return agent_get_next_ident(session, comment); - } +ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session, + char **comment) { + if (ssh_agent_get_ident_count(session) > 0) { + return ssh_agent_get_next_ident(session, comment); + } - return NULL; + return NULL; } /* caller has to free commment */ -struct ssh_public_key_struct *agent_get_next_ident(struct ssh_session_struct *session, +ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, char **comment) { - struct ssh_key_struct *key; - struct ssh_public_key_struct *pkey; - struct ssh_string_struct *blob = NULL; - struct ssh_string_struct *tmp = NULL; - int rc; + struct ssh_key_struct *key; + struct ssh_string_struct *blob = NULL; + struct ssh_string_struct *tmp = NULL; + int rc; - if (session->agent->count == 0) { - return NULL; - } - - switch(session->version) { - case 1: - return NULL; - case 2: - /* get the blob */ - blob = buffer_get_ssh_string(session->agent->ident); - if (blob == NULL) { + if (session->agent->count == 0) { return NULL; - } + } - /* get the comment */ - tmp = buffer_get_ssh_string(session->agent->ident); - if (tmp == NULL) { - ssh_string_free(blob); + switch(session->version) { + case 1: + return NULL; + case 2: + /* get the blob */ + blob = buffer_get_ssh_string(session->agent->ident); + if (blob == NULL) { + return NULL; + } - return NULL; - } + /* get the comment */ + tmp = buffer_get_ssh_string(session->agent->ident); + if (tmp == NULL) { + ssh_string_free(blob); - if (comment) { - *comment = ssh_string_to_char(tmp); - } else { - ssh_string_free(blob); - ssh_string_free(tmp); + return NULL; + } - return NULL; - } - ssh_string_free(tmp); + if (comment) { + *comment = ssh_string_to_char(tmp); + } else { + ssh_string_free(blob); + ssh_string_free(tmp); - /* get key from blob */ - rc = ssh_pki_import_pubkey_blob(blob, &key); - ssh_string_free(blob); - if (rc == SSH_ERROR) { - return NULL; - } - break; - default: - return NULL; - } + return NULL; + } + ssh_string_free(tmp); - pkey = ssh_pki_convert_key_to_publickey(key); - ssh_key_free(key); + /* get key from blob */ + rc = ssh_pki_import_pubkey_blob(blob, &key); + ssh_string_free(blob); + if (rc == SSH_ERROR) { + return NULL; + } + break; + default: + return NULL; + } - return pkey; + return key; } ssh_string agent_sign_data(struct ssh_session_struct *session, diff --git a/src/auth.c b/src/auth.c index 34d8a819..d9c153fa 100644 --- a/src/auth.c +++ b/src/auth.c @@ -803,190 +803,217 @@ fail: return SSH_AUTH_ERROR; } -/** - * @brief Try to authenticate through public key. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username to authenticate. You can specify NULL if - * ssh_option_set_username() has been used. You cannot try - * two different logins in a row. - * - * @param[in] publickey A public key returned by publickey_from_file(), or NULL - * to generate automatically from privatekey. - * - * @param[in] privatekey A private key returned by privatekey_from_file(). - * - * @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 successful. - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * @see publickey_from_file() - * @see privatekey_from_file() - * @see privatekey_free() - * @see ssh_userauth_offer_pubkey() - */ - -int ssh_userauth_pki_pubkey(ssh_session session, const char *username, - ssh_string publickey, ssh_key privatekey) { - ssh_string user = NULL; - ssh_string service = NULL; - ssh_string method = NULL; - ssh_string algo = NULL; - ssh_string sign = NULL; - ssh_key pubkey = NULL; - ssh_string pkstr = NULL; - int rc = SSH_AUTH_ERROR; - - if(session == NULL) { - return SSH_AUTH_ERROR; - } - - if(privatekey == NULL) { - ssh_set_error(session, SSH_FATAL, "invalid arguments"); - return SSH_AUTH_ERROR; - } - enter_function(); - -#if 0 - if (session->version == 1) { - return ssh_userauth1_pubkey(session, username, publickey, privatekey); - } -#endif - - if (username == NULL) { - if (session->username == NULL) { - if (ssh_options_apply(session) < 0) { - leave_function(); - return rc; - } - } - user = ssh_string_from_char(session->username); - } else { - user = ssh_string_from_char(username); - } - - if (user == NULL) { - ssh_set_error_oom(session); - leave_function(); - return rc; - } +#ifndef _WIN32 +static int ssh_userauth_agent_publickey(ssh_session session, + const char *username, + ssh_key pubkey) +{ + ssh_string str; + int rc; switch(session->pending_call_state) { case SSH_PENDING_CALL_NONE: break; - case SSH_PENDING_CALL_AUTH_PUBKEY: - ssh_string_free(user); - user = NULL; + case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY: goto pending; default: - ssh_set_error(session, SSH_FATAL, - "Bad call during pending SSH call in ssh_userauth_pubkey"); - goto error; - rc = SSH_ERROR; + 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; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); if (rc < 0) { - ssh_string_free(user); - leave_function(); - return rc; + goto fail; } - service = ssh_string_from_char("ssh-connection"); - if (service == NULL) { - ssh_set_error_oom(session); - goto error; + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->username); } - method = ssh_string_from_char("publickey"); - if (method == NULL) { - ssh_set_error_oom(session); - goto error; - } - algo = ssh_string_from_char(ssh_type_to_char(privatekey->type)); - if (algo == NULL) { - ssh_set_error_oom(session); - goto error; - } - if (publickey == NULL) { - pubkey = ssh_pki_publickey_from_privatekey(privatekey); - if (pubkey == NULL) { - /* most likely oom, and publickey_from_privatekey does not - * return any more information */ - ssh_set_error_oom(session); - goto error; - } - pkstr = publickey_to_string(ssh_pki_convert_key_to_publickey(pubkey)); - free(pubkey); - if (pkstr == NULL) { - /* same as above */ - ssh_set_error_oom(session); - goto error; - } + if (str == NULL) { + goto fail; } - /* we said previously the public key was accepted */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST) < 0 || - buffer_add_ssh_string(session->out_buffer, user) < 0 || - buffer_add_ssh_string(session->out_buffer, service) < 0 || - buffer_add_ssh_string(session->out_buffer, method) < 0 || - buffer_add_u8(session->out_buffer, 1) < 0 || - buffer_add_ssh_string(session->out_buffer, algo) < 0 || - buffer_add_ssh_string(session->out_buffer, (publickey == NULL ? pkstr : publickey)) < 0) { - ssh_set_error_oom(session); - goto error; + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; } - ssh_string_free(user); - ssh_string_free(service); - ssh_string_free(method); - ssh_string_free(algo); - ssh_string_free(pkstr); - - sign = ssh_pki_do_sign(session,session->out_buffer, privatekey); - if(sign == NULL) { - ssh_set_error_oom(session); - leave_function(); - return rc; + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; } - if (buffer_add_ssh_string(session->out_buffer, sign) < 0) { - ssh_set_error_oom(session); - ssh_string_free(sign); - leave_function(); - return rc; + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("publickey"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* private key? */ + rc = buffer_add_u8(session->out_buffer, 1); + if (rc < 0) { + goto fail; + } + + /* algo */ + str = ssh_string_from_char(pubkey->type_c); + if (rc < 0) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* public key */ + str = ssh_pki_export_pubkey_blob(pubkey); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* 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; } - ssh_string_free(sign); session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY; - if (packet_send(session) == SSH_ERROR) { - leave_function(); - return rc; + 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) + if (rc != SSH_AUTH_AGAIN) { session->pending_call_state = SSH_PENDING_CALL_NONE; - leave_function(); - return rc; + } -error: + return rc; +fail: + ssh_set_error_oom(session); buffer_reinit(session->out_buffer); - ssh_string_free(user); - ssh_string_free(service); - ssh_string_free(method); - ssh_string_free(algo); - ssh_string_free(pkstr); - leave_function(); - return rc; + return SSH_AUTH_ERROR; } +/** + * @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_optoins_set() only + * before you connect to the server. + */ +int ssh_userauth_agent(ssh_session session, + const char *username) +{ + ssh_key pubkey; + char *comment; + int rc; + + if (session == NULL) { + return SSH_AUTH_ERROR; + } + + if (!agent_is_running(session)) { + return SSH_AUTH_DENIED; + } + + for (pubkey = ssh_agent_get_first_ident(session, &comment); + pubkey != NULL; + pubkey = ssh_agent_get_next_ident(session, &comment)) { + ssh_log(session, SSH_LOG_RARE, "Trying identity %s", comment); + + rc = ssh_userauth_try_publickey(session, username, pubkey); + if (rc == SSH_AUTH_ERROR) { + ssh_string_free_char(comment); + ssh_key_free(pubkey); + return rc; + } else if (rc != SSH_AUTH_SUCCESS) { + ssh_log(session, SSH_LOG_PROTOCOL, "Public key of %s refused by server", comment); + ssh_string_free_char(comment); + ssh_key_free(pubkey); + continue; + } + + ssh_log(session, SSH_LOG_PROTOCOL, "Public key of %s accepted by server", comment); + + rc = ssh_userauth_agent_publickey(session, username, pubkey); + ssh_string_free_char(comment); + ssh_key_free(pubkey); + if (rc == SSH_AUTH_ERROR) { + return rc; + } else if (rc != SSH_AUTH_SUCCESS) { + ssh_log(session, + SSH_LOG_RARE, + "Server accepted public key but refused the signature"); + continue; + } + + return SSH_AUTH_SUCCESS; + } + + return SSH_AUTH_ERROR; +} +#endif + /** * @brief Try to authenticate through a private key file. * @@ -1057,138 +1084,31 @@ error: } #ifndef _WIN32 -/** - * @brief Try to authenticate through public key with an ssh agent. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username to authenticate. You can specify NULL if - * ssh_option_set_username() has been used. You cannot try - * two different logins in a row. - * - * @param[in] publickey The public key provided by the agent. - * - * @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 successful. - * - * @see publickey_from_file() - * @see privatekey_from_file() - * @see privatekey_free() - * @see ssh_userauth_offer_pubkey() - */ -int ssh_userauth_agent_pubkey(ssh_session session, const char *username, - ssh_public_key publickey) { - ssh_string user = NULL; - ssh_string service = NULL; - ssh_string method = NULL; - ssh_string algo = NULL; - ssh_string key = NULL; - ssh_string sign = NULL; - int rc = SSH_AUTH_ERROR; +int ssh_userauth_agent_pubkey(ssh_session session, + const char *username, + ssh_public_key publickey) +{ + ssh_key key; + int rc; - enter_function(); - - if (! agent_is_running(session)) { - return rc; - } - - if (username == NULL) { - if (session->username == NULL) { - if (ssh_options_apply(session) < 0) { - leave_function(); - return rc; - } + key = ssh_key_new(); + if (key == NULL) { + return SSH_AUTH_ERROR; } - user = ssh_string_from_char(session->username); - } else { - user = ssh_string_from_char(username); - } - if (user == NULL) { - ssh_set_error_oom(session); - leave_function(); + 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; + key->rsa = publickey->rsa_pub; + + rc = ssh_userauth_agent_publickey(session, username, key); + + key->dsa = NULL; + key->rsa = NULL; + ssh_key_free(key); + return rc; - } - - rc = ssh_userauth_request_service(session); - if (rc < 0) { - ssh_string_free(user); - leave_function(); - return rc; - } - - service = ssh_string_from_char("ssh-connection"); - if (service == NULL) { - ssh_set_error_oom(session); - goto error; - } - method = ssh_string_from_char("publickey"); - if (method == NULL) { - ssh_set_error_oom(session); - goto error; - } - algo = ssh_string_from_char(ssh_type_to_char(publickey->type)); - if (algo == NULL) { - ssh_set_error_oom(session); - goto error; - } - key = publickey_to_string(publickey); - if (key == NULL) { - ssh_set_error_oom(session); - goto error; - } - - /* we said previously the public key was accepted */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST) < 0 || - buffer_add_ssh_string(session->out_buffer, user) < 0 || - buffer_add_ssh_string(session->out_buffer, service) < 0 || - buffer_add_ssh_string(session->out_buffer, method) < 0 || - buffer_add_u8(session->out_buffer, 1) < 0 || - buffer_add_ssh_string(session->out_buffer, algo) < 0 || - buffer_add_ssh_string(session->out_buffer, key) < 0) { - ssh_set_error_oom(session); - goto error; - } - - sign = ssh_do_sign_with_agent(session, session->out_buffer, publickey); - - if (sign) { - if (buffer_add_ssh_string(session->out_buffer, sign) < 0) { - ssh_set_error_oom(session); - goto error; - } - ssh_string_free(sign); - session->auth_state=SSH_AUTH_STATE_NONE; - if (packet_send(session) == SSH_ERROR) { - leave_function(); - return rc; - } - rc = ssh_userauth_get_response(session); - } - - ssh_string_free(user); - ssh_string_free(service); - ssh_string_free(method); - ssh_string_free(algo); - ssh_string_free(key); - - leave_function(); - - return rc; -error: - buffer_reinit(session->out_buffer); - ssh_string_free(sign); - ssh_string_free(user); - ssh_string_free(service); - ssh_string_free(method); - ssh_string_free(algo); - ssh_string_free(key); - - leave_function(); - return rc; } #endif /* _WIN32 */ @@ -1373,69 +1293,14 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { /* Try authentication with ssh-agent first */ #ifndef _WIN32 - if (agent_is_running(session)) { - char *privkey_file = NULL; + rc = ssh_userauth_agent(session, NULL); + if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_SUCCESS) { + leave_function(); + return rc; + } - ssh_log(session, SSH_LOG_RARE, - "Trying to authenticate with SSH agent keys as user: %s", - session->username); - - for (pubkey = agent_get_first_ident(session, &privkey_file); - pubkey != NULL; - pubkey = agent_get_next_ident(session, &privkey_file)) { - - ssh_log(session, SSH_LOG_RARE, "Trying identity %s", privkey_file); - - pubkey_string = publickey_to_string(pubkey); - if (pubkey_string) { - rc = ssh_userauth_offer_pubkey(session, NULL, pubkey->type, pubkey_string); - ssh_string_free(pubkey_string); - if (rc == SSH_AUTH_ERROR) { - SAFE_FREE(privkey_file); - publickey_free(pubkey); - leave_function(); - - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - ssh_log(session, SSH_LOG_PROTOCOL, "Public key refused by server"); - SAFE_FREE(privkey_file); - publickey_free(pubkey); - continue; - } - ssh_log(session, SSH_LOG_PROTOCOL, "Public key accepted"); - /* pubkey accepted by server ! */ - rc = ssh_userauth_agent_pubkey(session, NULL, pubkey); - if (rc == SSH_AUTH_ERROR) { - SAFE_FREE(privkey_file); - publickey_free(pubkey); - leave_function(); - - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - ssh_log(session, SSH_LOG_RARE, - "Server accepted public key but refused the signature ;" - " It might be a bug of libssh"); - SAFE_FREE(privkey_file); - publickey_free(pubkey); - continue; - } - /* auth success */ - ssh_log(session, SSH_LOG_PROTOCOL, "Authentication using %s success", - privkey_file); - SAFE_FREE(privkey_file); - publickey_free(pubkey); - - leave_function(); - - return SSH_AUTH_SUCCESS; - } /* if pubkey */ - SAFE_FREE(privkey_file); - publickey_free(pubkey); - } /* for each privkey */ - } /* if agent is running */ #endif - for (it = ssh_list_get_iterator(session->identity); it != NULL; it = it->next) {