summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Morgan <brad@dmgctrl.com>2013-05-03 16:31:16 -0400
committerBrad Morgan <brad@dmgctrl.com>2013-05-04 11:28:20 -0400
commitf7158cd79b836d791b188f67b6d54064afb8ed31 (patch)
tree4b01ba9d4c784c52c682b0a1b44551421098d971
parentd04c384036ec6f843bbbb45a87d9d451c35cf29a (diff)
downloadlibgit2-f7158cd79b836d791b188f67b6d54064afb8ed31.tar.gz
Push working over ssh
-rw-r--r--include/git2/transport.h25
-rw-r--r--src/transports/cred.c77
-rw-r--r--src/transports/ssh.c74
3 files changed, 149 insertions, 27 deletions
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 9b9ecc5fc..31572c16a 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -27,6 +27,7 @@ GIT_BEGIN_DECL
typedef enum {
/* git_cred_userpass_plaintext */
GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
+ GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2,
} git_credtype_t;
/* The base structure for all credential types */
@@ -43,6 +44,14 @@ typedef struct git_cred_userpass_plaintext {
char *password;
} git_cred_userpass_plaintext;
+/* A plaintext username and password */
+typedef struct git_cred_ssh_keyfile_passphrase {
+ git_cred parent;
+ char *publickey;
+ char *privatekey;
+ char *passphrase;
+} git_cred_ssh_keyfile_passphrase;
+
/**
* Creates a new plain-text username and password credential object.
* The supplied credential parameter will be internally duplicated.
@@ -58,6 +67,22 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
const char *password);
/**
+ * Creates a new ssh key file and passphrase credential object.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param publickey The path to the public key of the credential.
+ * @param privatekey The path to 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_keyfile_passphrase_new(
+ git_cred **out,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase);
+
+/**
* Signature of a function which acquires a credential object.
*
* @param cred The newly created credential object.
diff --git a/src/transports/cred.c b/src/transports/cred.c
index ecb026062..83820ec05 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -58,3 +58,80 @@ int git_cred_userpass_plaintext_new(
*cred = &c->parent;
return 0;
}
+
+static void ssh_keyfile_passphrase_free(struct git_cred *cred)
+{
+ git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+ size_t pass_len = strlen(c->passphrase);
+
+ if (c->publickey) {
+ git__free(c->publickey);
+ }
+
+ git__free(c->privatekey);
+
+ if (c->passphrase) {
+ /* Zero the memory which previously held the passphrase */
+ memset(c->passphrase, 0x0, pass_len);
+ git__free(c->passphrase);
+ }
+
+ memset(c, 0, sizeof(*c));
+
+ git__free(c);
+}
+
+int git_cred_ssh_keyfile_passphrase_new(
+ git_cred **cred,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ git_cred_ssh_keyfile_passphrase *c;
+
+ if (!cred)
+ return -1;
+
+ c = git__malloc(sizeof(git_cred_ssh_keyfile_passphrase));
+ GITERR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
+ c->parent.free = ssh_keyfile_passphrase_free;
+
+ c->privatekey = git__strdup(privatekey);
+
+ if (!c->privatekey) {
+ git__free(c);
+ return -1;
+ }
+
+ if (publickey) {
+ c->publickey = git__strdup(publickey);
+
+ if (!c->publickey) {
+ git__free(c->privatekey);
+ git__free(c);
+ return -1;
+ }
+ } else {
+ c->publickey = NULL;
+ }
+
+ if (passphrase) {
+ c->passphrase = git__strdup(passphrase);
+
+ if (!c->passphrase) {
+ git__free(c->privatekey);
+ if (c->publickey) {
+ git__free(c->publickey);
+ }
+ git__free(c);
+ return -1;
+ }
+ } else {
+ c->passphrase = NULL;
+ }
+
+ *cred = &c->parent;
+ return 0;
+}
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 8f36a6549..935fe580a 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -8,6 +8,7 @@
#include "git2.h"
#include "buffer.h"
#include "netops.h"
+#include "smart.h"
#include <libssh2.h>
@@ -21,6 +22,8 @@ static const char cmd_receivepack[] = "git-receive-pack";
typedef struct {
git_smart_subtransport_stream parent;
gitno_socket socket;
+ LIBSSH2_SESSION *session;
+ LIBSSH2_CHANNEL *channel;
const char *cmd;
char *url;
unsigned sent_command : 1;
@@ -28,8 +31,9 @@ typedef struct {
typedef struct {
git_smart_subtransport parent;
- git_transport *owner;
+ transport_smart *owner;
ssh_stream *current_stream;
+ git_cred *cred;
} ssh_subtransport;
/*
@@ -40,27 +44,19 @@ typedef struct {
static int gen_proto(git_buf *request, const char *cmd, const char *url)
{
char *delim, *repo;
- char host[] = "host=";
size_t len;
- delim = strchr(url, '/');
+ delim = strchr(url, ':');
if (delim == NULL) {
giterr_set(GITERR_NET, "Malformed URL");
return -1;
}
- repo = delim;
-
- delim = strchr(url, ':');
- if (delim == NULL)
- delim = strchr(url, '/');
-
- len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
+ repo = delim+1;
+ len = strlen(cmd) + 1 + 1 + strlen(repo) + 1;
git_buf_grow(request, len);
- git_buf_printf(request, "%04x%s %s%c%s",
- (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
- git_buf_put(request, url, delim - url);
+ git_buf_printf(request, "%s '%s'", cmd, repo);
git_buf_putc(request, '\0');
if (git_buf_oom(request))
@@ -78,12 +74,18 @@ static int send_command(ssh_stream *s)
if (error < 0)
goto cleanup;
- /* It looks like negative values are errors here, and positive values
- * are the number of bytes sent. */
- error = gitno_send(&s->socket, request.ptr, request.size, 0);
+ error = libssh2_channel_process_startup(
+ s->channel,
+ "exec",
+ (uint32_t)sizeof("exec") - 1,
+ request.ptr,
+ request.size
+ );
+
+ if (0 != error)
+ goto cleanup;
- if (error >= 0)
- s->sent_command = 1;
+ s->sent_command = 1;
cleanup:
git_buf_free(&request);
@@ -97,19 +99,18 @@ static int ssh_stream_read(
size_t *bytes_read)
{
ssh_stream *s = (ssh_stream *)stream;
- gitno_buffer buf;
*bytes_read = 0;
if (!s->sent_command && send_command(s) < 0)
return -1;
- gitno_buffer_setup(&s->socket, &buf, buffer, buf_size);
+ int rc = libssh2_channel_read(s->channel, buffer, buf_size);
- if (gitno_recv(&buf) < 0)
+ if (rc < 0)
return -1;
- *bytes_read = buf.offset;
+ *bytes_read = rc;
return 0;
}
@@ -124,7 +125,12 @@ static int ssh_stream_write(
if (!s->sent_command && send_command(s) < 0)
return -1;
- return gitno_send(&s->socket, buffer, len, 0);
+ int rc = libssh2_channel_write(s->channel, buffer, len);
+ if (rc < 0) {
+ return -1;
+ }
+
+ return rc;
}
static void ssh_stream_free(git_smart_subtransport_stream *stream)
@@ -285,6 +291,17 @@ static int _git_receivepack_ls(
if (gitno_connect(&s->socket, host, "22", 0) < 0)
goto on_error;
+ if (t->owner->cred_acquire_cb(&t->cred,
+ t->owner->url,
+ user,
+ GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
+ t->owner->cred_acquire_payload) < 0)
+ return -1;
+
+ assert(t->cred);
+
+ git_cred_ssh_keyfile_passphrase *cred = (git_cred_ssh_keyfile_passphrase *)t->cred;
+
LIBSSH2_SESSION* session = libssh2_session_init();
if (!session)
goto on_error;
@@ -306,9 +323,9 @@ static int _git_receivepack_ls(
session,
user,
strlen(user),
- NULL,
- "/Users/bradfordmorgan/.ssh/id_rsa",
- NULL
+ cred->publickey,
+ cred->privatekey,
+ cred->passphrase
);
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
@@ -327,6 +344,9 @@ static int _git_receivepack_ls(
libssh2_channel_set_blocking(channel, 1);
+ s->session = session;
+ s->channel = channel;
+
t->current_stream = s;
git__free(host);
return 0;
@@ -412,7 +432,7 @@ int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owne
t = git__calloc(sizeof(ssh_subtransport), 1);
GITERR_CHECK_ALLOC(t);
- t->owner = owner;
+ t->owner = (transport_smart *)owner;
t->parent.action = _git_action;
t->parent.close = _git_close;
t->parent.free = _git_free;