summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-05-29 19:38:11 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2015-05-29 19:38:11 +0200
commit2c8550f040185fd416eb116c1a043caa8e623ed3 (patch)
treede79403b99a68d31a5732db1341ab65abba491c6
parent64be170d4786f5cc782d8d124cee0c1e2969d60a (diff)
parent8085adf82cbb57487d3ac9acdf0b5bcba92a9a66 (diff)
downloadlibgit2-2c8550f040185fd416eb116c1a043caa8e623ed3.tar.gz
Merge pull request #3157 from mgorny/ssh_memory_auth
Support getting SSH keys from memory, pt. 2
-rw-r--r--CMakeLists.txt5
-rw-r--r--include/git2/transport.h24
-rw-r--r--src/transports/cred.c50
-rw-r--r--src/transports/ssh.c22
-rw-r--r--tests/online/clone.c68
5 files changed, 168 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 181d30127..c3c7a1543 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -270,6 +270,11 @@ IF (LIBSSH2_FOUND)
LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2")
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
+
+ CHECK_LIBRARY_EXISTS(${LIBSSH2_LIBRARIES} libssh2_userauth_publickey_frommemory "" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
+ IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS)
+ ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS)
+ ENDIF()
ELSE()
MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
ENDIF()
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 99fd09a1b..2eeebd565 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -108,6 +108,13 @@ typedef enum {
* it will ask via this credential type.
*/
GIT_CREDTYPE_USERNAME = (1u << 5),
+
+ /**
+ * Credentials read from memory.
+ *
+ * Only available for libssh2+OpenSSL for now.
+ */
+ GIT_CREDTYPE_SSH_MEMORY = (1u << 6),
} git_credtype_t;
/* The base structure for all credential types */
@@ -291,6 +298,23 @@ GIT_EXTERN(int) git_cred_default_new(git_cred **out);
GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username);
/**
+ * Create a new ssh key credential object reading the keys from memory.
+ *
+ * @param out The newly created credential object.
+ * @param username username to use to authenticate.
+ * @param publickey The public key of the credential.
+ * @param privatekey The private key of the credential.
+ * @param passphrase The passphrase of the credential.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_cred_ssh_key_memory_new(
+ git_cred **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase);
+
+/**
* Signature of a function which acquires a credential object.
*
* - cred: The newly created credential object.
diff --git a/src/transports/cred.c b/src/transports/cred.c
index 8163d3115..006cd2c52 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -9,6 +9,14 @@
#include "smart.h"
#include "git2/cred_helpers.h"
+static int git_cred_ssh_key_type_new(
+ git_cred **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ git_credtype_t credtype);
+
int git_cred_has_username(git_cred *cred)
{
if (cred->credtype == GIT_CREDTYPE_DEFAULT)
@@ -31,6 +39,7 @@ const char *git_cred__username(git_cred *cred)
return c->username;
}
case GIT_CREDTYPE_SSH_KEY:
+ case GIT_CREDTYPE_SSH_MEMORY:
{
git_cred_ssh_key *c = (git_cred_ssh_key *) cred;
return c->username;
@@ -175,6 +184,45 @@ int git_cred_ssh_key_new(
const char *privatekey,
const char *passphrase)
{
+ return git_cred_ssh_key_type_new(
+ cred,
+ username,
+ publickey,
+ privatekey,
+ passphrase,
+ GIT_CREDTYPE_SSH_KEY);
+}
+
+int git_cred_ssh_key_memory_new(
+ git_cred **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+#ifdef GIT_SSH_MEMORY_CREDENTIALS
+ return git_cred_ssh_key_type_new(
+ cred,
+ username,
+ publickey,
+ privatekey,
+ passphrase,
+ GIT_CREDTYPE_SSH_MEMORY);
+#else
+ giterr_set(GITERR_INVALID,
+ "This version of libgit2 was not built with ssh memory credentials.");
+ return -1;
+#endif
+}
+
+static int git_cred_ssh_key_type_new(
+ git_cred **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ git_credtype_t credtype)
+{
git_cred_ssh_key *c;
assert(username && cred && privatekey);
@@ -182,7 +230,7 @@ int git_cred_ssh_key_new(
c = git__calloc(1, sizeof(git_cred_ssh_key));
GITERR_CHECK_ALLOC(c);
- c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
+ c->parent.credtype = credtype;
c->parent.free = ssh_key_free;
c->username = git__strdup(username);
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 55f715b1d..58f1aeb64 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -370,6 +370,25 @@ static int _git_ssh_authenticate_session(
session, c->username, c->prompt_callback);
break;
}
+#ifdef GIT_SSH_MEMORY_CREDENTIALS
+ case GIT_CREDTYPE_SSH_MEMORY: {
+ git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
+
+ assert(c->username);
+ assert(c->privatekey);
+
+ rc = libssh2_userauth_publickey_frommemory(
+ session,
+ c->username,
+ strlen(c->username),
+ c->publickey,
+ c->publickey ? strlen(c->publickey) : 0,
+ c->privatekey,
+ strlen(c->privatekey),
+ c->passphrase);
+ break;
+ }
+#endif
default:
rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
}
@@ -740,6 +759,9 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use
if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
*out |= GIT_CREDTYPE_SSH_KEY;
*out |= GIT_CREDTYPE_SSH_CUSTOM;
+#ifdef GIT_SSH_MEMORY_CREDENTIALS
+ *out |= GIT_CREDTYPE_SSH_MEMORY;
+#endif
ptr += strlen(SSH_AUTH_PUBLICKEY);
continue;
}
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 1930a8ba3..fa0dbd69c 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -501,6 +501,74 @@ void test_online_clone__ssh_cert(void)
cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options));
}
+static char *read_key_file(const char *path)
+{
+ FILE *f;
+ char *buf;
+ long key_length;
+
+ if (!path || !*path)
+ return NULL;
+
+ cl_assert((f = fopen(path, "r")) != NULL);
+ cl_assert(fseek(f, 0, SEEK_END) != -1);
+ cl_assert((key_length = ftell(f)) != -1);
+ cl_assert(fseek(f, 0, SEEK_SET) != -1);
+ cl_assert((buf = malloc(key_length)) != NULL);
+ cl_assert(fread(buf, key_length, 1, f) == 1);
+ fclose(f);
+
+ return buf;
+}
+
+static int ssh_memory_cred_cb(git_cred **cred, const char *url, const char *user_from_url,
+ unsigned int allowed_types, void *payload)
+{
+ const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ const char *pubkey_path = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
+ const char *privkey_path = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+ const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+
+ GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload);
+
+ if (allowed_types & GIT_CREDTYPE_USERNAME)
+ return git_cred_username_new(cred, remote_user);
+
+ if (allowed_types & GIT_CREDTYPE_SSH_KEY)
+ {
+ char *pubkey = read_key_file(pubkey_path);
+ char *privkey = read_key_file(privkey_path);
+
+ int ret = git_cred_ssh_key_memory_new(cred, remote_user, pubkey, privkey, passphrase);
+
+ if (privkey)
+ free(privkey);
+ if (pubkey)
+ free(pubkey);
+ return ret;
+ }
+
+ giterr_set(GITERR_NET, "unexpected cred type");
+ return -1;
+}
+
+void test_online_clone__ssh_memory_auth(void)
+{
+ const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+
+#ifndef GIT_SSH_MEMORY_CREDENTIALS
+ clar__skip();
+#endif
+ if (!remote_url || !remote_user || !privkey || strncmp(remote_url, "ssh://", 5) != 0)
+ clar__skip();
+
+ g_options.fetch_opts.callbacks.credentials = ssh_memory_cred_cb;
+
+ cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
+}
+
void test_online_clone__url_with_no_path_returns_EINVALIDSPEC(void)
{
cl_git_fail_with(git_clone(&g_repo, "http://github.com", "./foo", &g_options),