Commit a2e873d1a15df03d7ecfac2a1bec56cf66b8cc40

Vicent Martí 2013-11-20T07:06:35

Merge pull request #1972 from ghedo/ssh_agent ssh: add support for ssh-agent authentication

diff --git a/include/git2/transport.h b/include/git2/transport.h
index caabd04..0393210 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -131,6 +131,18 @@ GIT_EXTERN(int) git_cred_ssh_key_new(
 	const char *passphrase);
 
 /**
+ * Create a new ssh key credential object used for querying an ssh-agent.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username username to use to authenticate
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_cred_ssh_key_from_agent(
+	git_cred **out,
+	const char *username);
+
+/**
  * Create an ssh key credential with a custom signing function.
  *
  * This lets you use your own function to sign the challenge.
diff --git a/src/transports/cred.c b/src/transports/cred.c
index 05d2c8d..abae70b 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -165,6 +165,28 @@ int git_cred_ssh_key_new(
 	return 0;
 }
 
+int git_cred_ssh_key_from_agent(git_cred **cred, const char *username) {
+	git_cred_ssh_key *c;
+
+	assert(cred);
+
+	c = git__calloc(1, sizeof(git_cred_ssh_key));
+	GITERR_CHECK_ALLOC(c);
+
+	c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
+	c->parent.free = ssh_key_free;
+
+	if (username) {
+		c->username = git__strdup(username);
+		GITERR_CHECK_ALLOC(c->username);
+	}
+
+	c->privatekey = NULL;
+
+	*cred = &c->parent;
+	return 0;
+}
+
 int git_cred_ssh_custom_new(
 	git_cred **cred,
 	const char *username,
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 4a905e3..37f1708 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -235,6 +235,50 @@ static int git_ssh_extract_url_parts(
 	return 0;
 }
 
+static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) {
+	int rc = LIBSSH2_ERROR_NONE;
+
+	struct libssh2_agent_publickey *curr, *prev = NULL;
+
+	LIBSSH2_AGENT *agent = libssh2_agent_init(session);
+
+	if (agent == NULL)
+		return -1;
+
+	rc = libssh2_agent_connect(agent);
+
+	if (rc != LIBSSH2_ERROR_NONE)
+		goto shutdown;
+
+	rc = libssh2_agent_list_identities(agent);
+
+	if (rc != LIBSSH2_ERROR_NONE)
+		goto shutdown;
+
+	while (1) {
+		rc = libssh2_agent_get_identity(agent, &curr, prev);
+
+		if (rc < 0)
+			goto shutdown;
+
+		if (rc == 1)
+			goto shutdown;
+
+		rc = libssh2_agent_userauth(agent, c->username, curr);
+
+		if (rc == 0)
+			break;
+
+		prev = curr;
+	}
+
+shutdown:
+	libssh2_agent_disconnect(agent);
+	libssh2_agent_free(agent);
+
+	return rc;
+}
+
 static int _git_ssh_authenticate_session(
 	LIBSSH2_SESSION* session,
 	const char *user,
@@ -253,8 +297,14 @@ static int _git_ssh_authenticate_session(
 		case GIT_CREDTYPE_SSH_KEY: {
 			git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
 			user = c->username ? c->username : user;
-			rc = libssh2_userauth_publickey_fromfile(
-				session, c->username, c->publickey, c->privatekey, c->passphrase);
+
+			if (c->privatekey)
+				rc = libssh2_userauth_publickey_fromfile(
+					session, c->username, c->publickey,
+					c->privatekey, c->passphrase);
+			else
+				rc = ssh_agent_auth(session, c);
+
 			break;
 		}
 		case GIT_CREDTYPE_SSH_CUSTOM: {