aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/curl/curl.h1
-rw-r--r--lib/ssh.c114
-rw-r--r--lib/ssh.h9
3 files changed, 121 insertions, 3 deletions
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 2cad28298..46ccf164b 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -631,6 +631,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 4c0d48f43..720610259 100644
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -324,7 +324,8 @@ static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc)
static LIBSSH2_FREE_FUNC(my_libssh2_free)
{
(void)abstract; /* arg not used */
- free(ptr);
+ if(ptr) /* ssh2 agent sometimes call free with null ptr */
+ free(ptr);
}
/*
@@ -345,6 +346,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",
@@ -893,12 +897,97 @@ 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_KEY_INIT);
+ 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 long 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:
@@ -2357,6 +2446,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 dce035b5e..bf43fdf3a 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