cURL / Mailing Lists / curl-library / Single Mail

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

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