Commit 22618906 by Carlos Martín Nieto

ssh: detect authentication methods

Before calling the credentials callback, ask the sever which
authentication methods it supports and report that to the user, instead
of simply reporting everything that the transport supports.

In case of an error, we do fall back to listing all of them.
parent 76f76162
...@@ -37,6 +37,8 @@ typedef struct { ...@@ -37,6 +37,8 @@ typedef struct {
git_cred *cred; git_cred *cred;
} ssh_subtransport; } ssh_subtransport;
static int list_auth_methods(int *out, const char *host, const char *port);
static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg) static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
{ {
char *ssherr; char *ssherr;
...@@ -387,7 +389,7 @@ static int _git_ssh_setup_conn( ...@@ -387,7 +389,7 @@ static int _git_ssh_setup_conn(
{ {
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *default_port="22"; const char *default_port="22";
int no_callback = 0; int no_callback = 0, auth_methods;
ssh_stream *s; ssh_stream *s;
LIBSSH2_SESSION* session=NULL; LIBSSH2_SESSION* session=NULL;
LIBSSH2_CHANNEL* channel=NULL; LIBSSH2_CHANNEL* channel=NULL;
...@@ -408,6 +410,12 @@ static int _git_ssh_setup_conn( ...@@ -408,6 +410,12 @@ static int _git_ssh_setup_conn(
GITERR_CHECK_ALLOC(port); GITERR_CHECK_ALLOC(port);
} }
if (list_auth_methods(&auth_methods, host, port) < 0) {
auth_methods = GIT_CREDTYPE_USERPASS_PLAINTEXT |
GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM |
GIT_CREDTYPE_SSH_INTERACTIVE;
}
if (gitno_connect(&s->socket, host, port, 0) < 0) if (gitno_connect(&s->socket, host, port, 0) < 0)
goto on_error; goto on_error;
...@@ -418,10 +426,7 @@ static int _git_ssh_setup_conn( ...@@ -418,10 +426,7 @@ static int _git_ssh_setup_conn(
no_callback = 1; no_callback = 1;
} else { } else {
int error; int error;
error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, user, error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, user, auth_methods,
GIT_CREDTYPE_USERPASS_PLAINTEXT |
GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM |
GIT_CREDTYPE_SSH_INTERACTIVE,
t->owner->cred_acquire_payload); t->owner->cred_acquire_payload);
if (error == GIT_PASSTHROUGH) if (error == GIT_PASSTHROUGH)
...@@ -585,6 +590,72 @@ static void _ssh_free(git_smart_subtransport *subtransport) ...@@ -585,6 +590,72 @@ static void _ssh_free(git_smart_subtransport *subtransport)
git__free(t); git__free(t);
} }
#define SSH_AUTH_PUBLICKEY "publickey"
#define SSH_AUTH_PASSWORD "password"
#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
static int list_auth_methods(int *out, const char *host, const char *port)
{
gitno_socket s;
LIBSSH2_SESSION *session = NULL;
const char *list, *ptr;
*out = 0;
if (gitno_connect(&s, host, port, 0) < 0)
return -1;
if (_git_ssh_session_create(&session, s) < 0)
goto on_error;
/* the username is dummy, we're throwing away the connection anyway */
list = libssh2_userauth_list(session, "git", strlen("git"));
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
if (list == NULL)
goto out;
ptr = list;
while (ptr) {
if (*ptr == ',')
ptr++;
if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
*out |= GIT_CREDTYPE_SSH_KEY;
*out |= GIT_CREDTYPE_SSH_CUSTOM;
ptr += strlen(SSH_AUTH_PUBLICKEY);
continue;
}
if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
*out |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
ptr += strlen(SSH_AUTH_PASSWORD);
continue;
}
if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
*out |= GIT_CREDTYPE_SSH_INTERACTIVE;
ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
continue;
}
/* Skipt it if we don't know it */
ptr = strchr(ptr, ',');
}
out:
libssh2_session_free(session);
gitno_close(&s);
return 0;
on_error:
libssh2_session_free(session);
gitno_close(&s);
return -1;
}
#endif #endif
int git_smart_subtransport_ssh( int git_smart_subtransport_ssh(
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git" #define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git"
#define ASSEMBLA_REPO_URL "https://libgit2:_Libgit2@git.assembla.com/libgit2-test-repos.git" #define ASSEMBLA_REPO_URL "https://libgit2:_Libgit2@git.assembla.com/libgit2-test-repos.git"
#define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
static git_repository *g_repo; static git_repository *g_repo;
static git_clone_options g_options; static git_clone_options g_options;
...@@ -313,7 +315,20 @@ void test_online_clone__can_cancel(void) ...@@ -313,7 +315,20 @@ void test_online_clone__can_cancel(void)
} }
static int check_ssh_auth_methods(git_cred **cred, const char *url, const char *username_from_url,
unsigned int allowed_types, void *data)
{
GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(data);
cl_assert_equal_i(GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM, allowed_types);
return GIT_EUSER;
}
void test_online_clone__ssh_auth_methods(void)
{
g_options.remote_callbacks.credentials = check_ssh_auth_methods;
cl_git_fail_with(GIT_EUSER,
git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment