curl-library
Re: Re: Adding SSH agent / pageant integration?
From: Ellié Computing Open Source Program <opensource_at_elliecomputing.com>
Date: Fri, 9 Mar 2012 17:38:03 +0100
Date: Fri, 9 Mar 2012 17:38:03 +0100
Hi,
here is what I ended up for agent based authentication, I could test it on my windows box and a linux vm against a WinSCP server.
----------------------
From: Armel Asselin <armelasselin_at_hotmail.com>
Date: Fri, 9 Mar 2012 17:24:42 +0100
Subject: [PATCH] added agent based authentication
--- include/curl/curl.h | 1 + lib/ssh.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++- lib/ssh.h | 9 ++++ 3 files changed, 125 insertions(+), 1 deletions(-) diff --git a/include/curl/curl.h b/include/curl/curl.h index f2501cd..db17e42 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -617,6 +617,7 @@ typedef enum { #define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ #define CURLSSH_AUTH_HOST (1<<2) /* host key files */ #define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ #define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY #define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ diff --git a/lib/ssh.c b/lib/ssh.c index 20e9848..3296f0b 100644 --- a/lib/ssh.c +++ b/lib/ssh.c @@ -318,6 +318,7 @@ static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc) static LIBSSH2_FREE_FUNC(my_libssh2_free) { (void)abstract; /* arg not used */ + if(ptr) /* ssh2 agent sometimes call free with null ptr */ free(ptr); } @@ -339,6 +340,9 @@ static void state(struct connectdata *conn, sshstate nowstate) "SSH_AUTH_PKEY", "SSH_AUTH_PASS_INIT", "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", "SSH_AUTH_HOST_INIT", "SSH_AUTH_HOST", "SSH_AUTH_KEY_INIT", @@ -889,12 +893,103 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) state(conn, SSH_AUTH_HOST); } else { - state(conn, SSH_AUTH_KEY_INIT); + state(conn, SSH_AUTH_AGENT_INIT); } break; case SSH_AUTH_HOST: + state(conn, SSH_AUTH_AGENT_INIT); + break; + + case SSH_AUTH_AGENT_INIT: +#ifdef HAVE_LIBSSH2_AGENT_API + if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) + && (strstr(sshc->authlist, "publickey") != NULL)) { + + /* Connect to the ssh-agent */ + /* The agent could be shared by a curl thread i believe + but nothing obvious as keys can be added/removed at any time */ + if(!sshc->ssh_agent) { + sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); + if(!sshc->ssh_agent) { + infof(data, "Could not create agent object\n"); + + state(conn, SSH_AUTH_KEY_INIT); + } + } + + rc = libssh2_agent_connect(sshc->ssh_agent); + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + if(rc < 0) { + infof(data, "Failure connecting to agent\n"); + state(conn, SSH_AUTH_KEY_INIT); + } + else { + state(conn, SSH_AUTH_AGENT_LIST); + } + } + else +#endif /* HAVE_LIBSSH2_AGENT_API */ + { + state(conn, SSH_AUTH_KEY_INIT); + } + break; + + case SSH_AUTH_AGENT_LIST: + { + rc = libssh2_agent_list_identities(sshc->ssh_agent); + + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + if(rc < 0) { + infof(data, "Failure requesting identities to agent\n"); + state(conn, SSH_AUTH_KEY_INIT); + } + else { + state(conn, SSH_AUTH_AGENT); + sshc->sshagent_prev_identity = NULL; + } + break; + } + + case SSH_AUTH_AGENT: + /* as prev_identity evolves only after an identity user auth finished + we can safely request it again as lons as EAGAIN is returned + here or by libssh2_agent_userauth */ + rc = libssh2_agent_get_identity(sshc->ssh_agent, + &sshc->sshagent_identity, sshc->sshagent_prev_identity); + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + + if(rc == 0) { + rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + sshc->sshagent_identity); + + if(rc < 0) { + if(rc != LIBSSH2_ERROR_EAGAIN) { + /* tried and failed? go to next identity */ + sshc->sshagent_prev_identity = sshc->sshagent_identity; + } + break; + } + } + + if(rc < 0) { + infof(data, "Failure requesting identities to agent\n"); + } + else if(rc == 1) { + infof(data, "No identity would match\n"); + } + + if(rc == LIBSSH2_ERROR_NONE) { + sshc->authed = TRUE; + infof(data, "Agent based authentication successful\n"); + state(conn, SSH_AUTH_DONE); + } + else { state(conn, SSH_AUTH_KEY_INIT); + } break; case SSH_AUTH_KEY_INIT: @@ -2403,6 +2498,25 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } #endif +#ifdef HAVE_LIBSSH2_AGENT_API + if(sshc->ssh_agent) { + rc = libssh2_agent_disconnect(sshc->ssh_agent); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + else if(rc < 0) { + infof(data, "Failed to disconnect from libssh2 agent\n"); + } + libssh2_agent_free (sshc->ssh_agent); + sshc->ssh_agent = NULL; + + /* NB: there is no need to free identities, they are part of internal + agent stuff */ + sshc->sshagent_identity = NULL; + sshc->sshagent_prev_identity = NULL; + } +#endif + if(sshc->ssh_session) { rc = libssh2_session_free(sshc->ssh_session); if(rc == LIBSSH2_ERROR_EAGAIN) { diff --git a/lib/ssh.h b/lib/ssh.h index dce035b..bf43fdf 100644 --- a/lib/ssh.h +++ b/lib/ssh.h @@ -44,6 +44,9 @@ typedef enum { SSH_AUTH_PKEY, SSH_AUTH_PASS_INIT, SSH_AUTH_PASS, + SSH_AUTH_AGENT_INIT,/* initialize then wait for connection to agent */ + SSH_AUTH_AGENT_LIST,/* ask for list then wait for entire list to come */ + SSH_AUTH_AGENT, /* attempt one key at a time */ SSH_AUTH_HOST_INIT, SSH_AUTH_HOST, SSH_AUTH_KEY_INIT, @@ -139,6 +142,12 @@ struct ssh_conn { LIBSSH2_SFTP_HANDLE *sftp_handle; int orig_waitfor; /* default READ/WRITE bits wait for */ +#ifdef HAVE_LIBSSH2_AGENT_API + LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */ + struct libssh2_agent_publickey *sshagent_identity, + *sshagent_prev_identity; +#endif + /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h header */ #ifdef HAVE_LIBSSH2_KNOWNHOST_API -- 1.7.9.msysgit.0
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2012-03-09