mirror of
https://git.proxmox.com/git/libgit2
synced 2026-03-24 11:05:16 +00:00
Merge branch 'cmn/ssh-retry'
This commit is contained in:
commit
15c7da3442
@ -15,6 +15,12 @@ v0.21 + 1
|
|||||||
* The git_transport structure definition has moved into the sys/transport.h
|
* The git_transport structure definition has moved into the sys/transport.h
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
* The ssh transport supports asking the remote host for accepted
|
||||||
|
credential types as well as multiple challeges using a single
|
||||||
|
connection. This requires to know which username you want to connect
|
||||||
|
as, so this introduces the USERNAME credential type which the ssh
|
||||||
|
transport will use to ask for the username.
|
||||||
|
|
||||||
* The git_transport_register function no longer takes a priority and takes
|
* The git_transport_register function no longer takes a priority and takes
|
||||||
a URL scheme name (eg "http") instead of a prefix like "http://"
|
a URL scheme name (eg "http") instead of a prefix like "http://"
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,7 @@ typedef enum {
|
|||||||
GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */
|
GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */
|
||||||
GIT_ELOCKED = -14, /**< Lock file prevented operation */
|
GIT_ELOCKED = -14, /**< Lock file prevented operation */
|
||||||
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
|
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
|
||||||
|
GIT_EAUTH = -16, /**< Authentication error */
|
||||||
|
|
||||||
GIT_PASSTHROUGH = -30, /**< Internal only */
|
GIT_PASSTHROUGH = -30, /**< Internal only */
|
||||||
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
|
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
|
||||||
|
|||||||
@ -36,6 +36,14 @@ typedef enum {
|
|||||||
|
|
||||||
/* git_cred_ssh_interactive */
|
/* git_cred_ssh_interactive */
|
||||||
GIT_CREDTYPE_SSH_INTERACTIVE = (1u << 4),
|
GIT_CREDTYPE_SSH_INTERACTIVE = (1u << 4),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Username-only information
|
||||||
|
*
|
||||||
|
* If the SSH transport does not know which username to use,
|
||||||
|
* it will ask via this credential type.
|
||||||
|
*/
|
||||||
|
GIT_CREDTYPE_USERNAME = (1u << 5),
|
||||||
} git_credtype_t;
|
} git_credtype_t;
|
||||||
|
|
||||||
/* The base structure for all credential types */
|
/* The base structure for all credential types */
|
||||||
@ -103,6 +111,12 @@ typedef struct git_cred_ssh_custom {
|
|||||||
/** A key for NTLM/Kerberos "default" credentials */
|
/** A key for NTLM/Kerberos "default" credentials */
|
||||||
typedef struct git_cred git_cred_default;
|
typedef struct git_cred git_cred_default;
|
||||||
|
|
||||||
|
/** Username-only credential information */
|
||||||
|
typedef struct git_cred_username {
|
||||||
|
git_cred parent;
|
||||||
|
char username[1];
|
||||||
|
} git_cred_username;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a credential object contains username information.
|
* Check whether a credential object contains username information.
|
||||||
*
|
*
|
||||||
@ -204,6 +218,14 @@ GIT_EXTERN(int) git_cred_ssh_custom_new(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_cred_default_new(git_cred **out);
|
GIT_EXTERN(int) git_cred_default_new(git_cred **out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a credential to specify a username.
|
||||||
|
*
|
||||||
|
* This is used with ssh authentication to query for the username if
|
||||||
|
* none is specified in the url.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature of a function which acquires a credential object.
|
* Signature of a function which acquires a credential object.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -22,7 +22,13 @@ ctest -V . || exit $?
|
|||||||
# can do the push tests over it
|
# can do the push tests over it
|
||||||
|
|
||||||
killall git-daemon
|
killall git-daemon
|
||||||
sudo start ssh
|
|
||||||
|
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||||
|
echo 'PasswordAuthentication yes' | sudo tee -a /etc/sshd_config
|
||||||
|
else
|
||||||
|
sudo start ssh
|
||||||
|
fi
|
||||||
|
|
||||||
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
|
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
|
||||||
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
|
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
|
||||||
ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
|
ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
|
||||||
@ -34,7 +40,7 @@ export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
|
|||||||
export GITTEST_REMOTE_SSH_PASSPHRASE=""
|
export GITTEST_REMOTE_SSH_PASSPHRASE=""
|
||||||
|
|
||||||
if [ -e ./libgit2_clar ]; then
|
if [ -e ./libgit2_clar ]; then
|
||||||
./libgit2_clar -sonline::push -sonline::clone::cred_callback_failure &&
|
./libgit2_clar -sonline::push -sonline::clone::cred_callback &&
|
||||||
rm -rf $HOME/_temp/test.git &&
|
rm -rf $HOME/_temp/test.git &&
|
||||||
git init --bare $HOME/_temp/test.git && # create an empty one
|
git init --bare $HOME/_temp/test.git && # create an empty one
|
||||||
./libgit2_clar -sonline::clone::ssh_with_paths
|
./libgit2_clar -sonline::clone::ssh_with_paths
|
||||||
|
|||||||
@ -17,6 +17,40 @@ int git_cred_has_username(git_cred *cred)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *git_cred__username(git_cred *cred)
|
||||||
|
{
|
||||||
|
switch (cred->credtype) {
|
||||||
|
case GIT_CREDTYPE_USERNAME:
|
||||||
|
{
|
||||||
|
git_cred_username *c = (git_cred_username *) cred;
|
||||||
|
return c->username;
|
||||||
|
}
|
||||||
|
case GIT_CREDTYPE_USERPASS_PLAINTEXT:
|
||||||
|
{
|
||||||
|
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *) cred;
|
||||||
|
return c->username;
|
||||||
|
}
|
||||||
|
case GIT_CREDTYPE_SSH_KEY:
|
||||||
|
{
|
||||||
|
git_cred_ssh_key *c = (git_cred_ssh_key *) cred;
|
||||||
|
return c->username;
|
||||||
|
}
|
||||||
|
case GIT_CREDTYPE_SSH_CUSTOM:
|
||||||
|
{
|
||||||
|
git_cred_ssh_custom *c = (git_cred_ssh_custom *) cred;
|
||||||
|
return c->username;
|
||||||
|
}
|
||||||
|
case GIT_CREDTYPE_SSH_INTERACTIVE:
|
||||||
|
{
|
||||||
|
git_cred_ssh_interactive *c = (git_cred_ssh_interactive *) cred;
|
||||||
|
return c->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void plaintext_free(struct git_cred *cred)
|
static void plaintext_free(struct git_cred *cred)
|
||||||
{
|
{
|
||||||
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
|
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
|
||||||
@ -129,6 +163,11 @@ static void default_free(struct git_cred *cred)
|
|||||||
git__free(c);
|
git__free(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void username_free(struct git_cred *cred)
|
||||||
|
{
|
||||||
|
git__free(cred);
|
||||||
|
}
|
||||||
|
|
||||||
int git_cred_ssh_key_new(
|
int git_cred_ssh_key_new(
|
||||||
git_cred **cred,
|
git_cred **cred,
|
||||||
const char *username,
|
const char *username,
|
||||||
@ -263,3 +302,22 @@ int git_cred_default_new(git_cred **cred)
|
|||||||
*cred = c;
|
*cred = c;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_cred_username_new(git_cred **cred, const char *username)
|
||||||
|
{
|
||||||
|
git_cred_username *c;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
assert(cred);
|
||||||
|
|
||||||
|
len = strlen(username);
|
||||||
|
c = git__malloc(sizeof(git_cred_username) + len + 1);
|
||||||
|
GITERR_CHECK_ALLOC(c);
|
||||||
|
|
||||||
|
c->parent.credtype = GIT_CREDTYPE_USERNAME;
|
||||||
|
c->parent.free = username_free;
|
||||||
|
memcpy(c->username, username, len + 1);
|
||||||
|
|
||||||
|
*cred = (git_cred *) c;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
14
src/transports/cred.h
Normal file
14
src/transports/cred.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||||
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDE_git_cred_h__
|
||||||
|
#define INCLUDE_git_cred_h__
|
||||||
|
|
||||||
|
#include "git2/transport.h"
|
||||||
|
|
||||||
|
const char *git_cred__username(git_cred *cred);
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -41,6 +41,9 @@ int git_cred_userpass(
|
|||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (GIT_CREDTYPE_USERNAME & allowed_types)
|
||||||
|
return git_cred_username_new(cred, effective_username);
|
||||||
|
|
||||||
if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
|
if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
|
||||||
git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
|
git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "netops.h"
|
#include "netops.h"
|
||||||
#include "smart.h"
|
#include "smart.h"
|
||||||
|
#include "cred.h"
|
||||||
|
|
||||||
#ifdef GIT_SSH
|
#ifdef GIT_SSH
|
||||||
|
|
||||||
@ -41,6 +42,8 @@ typedef struct {
|
|||||||
char *cmd_receivepack;
|
char *cmd_receivepack;
|
||||||
} ssh_subtransport;
|
} ssh_subtransport;
|
||||||
|
|
||||||
|
static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
|
||||||
|
|
||||||
static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
|
static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
|
||||||
{
|
{
|
||||||
char *ssherr;
|
char *ssherr;
|
||||||
@ -354,6 +357,9 @@ static int _git_ssh_authenticate_session(
|
|||||||
}
|
}
|
||||||
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
|
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
|
||||||
|
|
||||||
|
if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
|
||||||
|
return GIT_EAUTH;
|
||||||
|
|
||||||
if (rc != LIBSSH2_ERROR_NONE) {
|
if (rc != LIBSSH2_ERROR_NONE) {
|
||||||
ssh_error(session, "Failed to authenticate SSH session");
|
ssh_error(session, "Failed to authenticate SSH session");
|
||||||
return -1;
|
return -1;
|
||||||
@ -362,6 +368,43 @@ static int _git_ssh_authenticate_session(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, int auth_methods)
|
||||||
|
{
|
||||||
|
int error, no_callback = 0;
|
||||||
|
git_cred *cred = NULL;
|
||||||
|
|
||||||
|
if (!t->owner->cred_acquire_cb) {
|
||||||
|
no_callback = 1;
|
||||||
|
} else {
|
||||||
|
error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods,
|
||||||
|
t->owner->cred_acquire_payload);
|
||||||
|
|
||||||
|
if (error == GIT_PASSTHROUGH)
|
||||||
|
no_callback = 1;
|
||||||
|
else if (error < 0)
|
||||||
|
return error;
|
||||||
|
else if (!cred) {
|
||||||
|
giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (no_callback) {
|
||||||
|
giterr_set(GITERR_SSH, "authentication required but no callback set");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cred->credtype & auth_methods)) {
|
||||||
|
cred->free(cred);
|
||||||
|
giterr_set(GITERR_SSH, "callback returned unsupported credentials type");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = cred;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int _git_ssh_session_create(
|
static int _git_ssh_session_create(
|
||||||
LIBSSH2_SESSION** session,
|
LIBSSH2_SESSION** session,
|
||||||
gitno_socket socket)
|
gitno_socket socket)
|
||||||
@ -402,8 +445,9 @@ 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 auth_methods, error = 0;
|
||||||
ssh_stream *s;
|
ssh_stream *s;
|
||||||
|
git_cred *cred = NULL;
|
||||||
LIBSSH2_SESSION* session=NULL;
|
LIBSSH2_SESSION* session=NULL;
|
||||||
LIBSSH2_CHANNEL* channel=NULL;
|
LIBSSH2_CHANNEL* channel=NULL;
|
||||||
|
|
||||||
@ -414,56 +458,68 @@ static int _git_ssh_setup_conn(
|
|||||||
s = (ssh_stream *)*stream;
|
s = (ssh_stream *)*stream;
|
||||||
|
|
||||||
if (!git__prefixcmp(url, prefix_ssh)) {
|
if (!git__prefixcmp(url, prefix_ssh)) {
|
||||||
if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0)
|
if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
} else {
|
} else {
|
||||||
if (git_ssh_extract_url_parts(&host, &user, url) < 0)
|
if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
port = git__strdup(default_port);
|
port = git__strdup(default_port);
|
||||||
GITERR_CHECK_ALLOC(port);
|
GITERR_CHECK_ALLOC(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gitno_connect(&s->socket, host, port, 0) < 0)
|
/* we need the username to ask for auth methods */
|
||||||
|
if (!user) {
|
||||||
|
if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
user = git__strdup(((git_cred_username *) cred)->username);
|
||||||
|
cred->free(cred);
|
||||||
|
cred = NULL;
|
||||||
|
if (!user)
|
||||||
|
goto on_error;
|
||||||
|
} else if (user && pass) {
|
||||||
|
if ((error = git_cred_userpass_plaintext_new(&cred, user, pass)) < 0)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = gitno_connect(&s->socket, host, port, 0)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
if (user && pass) {
|
if ((error = _git_ssh_session_create(&session, s->socket)) < 0)
|
||||||
if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
|
goto on_error;
|
||||||
goto on_error;
|
|
||||||
} else if (!t->owner->cred_acquire_cb) {
|
|
||||||
no_callback = 1;
|
|
||||||
} else {
|
|
||||||
int error;
|
|
||||||
error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, user,
|
|
||||||
GIT_CREDTYPE_USERPASS_PLAINTEXT |
|
|
||||||
GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM |
|
|
||||||
GIT_CREDTYPE_SSH_INTERACTIVE,
|
|
||||||
t->owner->cred_acquire_payload);
|
|
||||||
|
|
||||||
if (error == GIT_PASSTHROUGH)
|
if ((error = list_auth_methods(&auth_methods, session, user)) < 0)
|
||||||
no_callback = 1;
|
goto on_error;
|
||||||
else if (error < 0)
|
|
||||||
|
error = GIT_EAUTH;
|
||||||
|
/* if we already have something to try */
|
||||||
|
if (cred && auth_methods & cred->credtype)
|
||||||
|
error = _git_ssh_authenticate_session(session, cred);
|
||||||
|
|
||||||
|
while (error == GIT_EAUTH) {
|
||||||
|
if (cred) {
|
||||||
|
cred->free(cred);
|
||||||
|
cred = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = request_creds(&cred, t, user, auth_methods)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
else if (!t->cred) {
|
|
||||||
giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
|
if (strcmp(user, git_cred__username(cred))) {
|
||||||
|
giterr_set(GITERR_SSH, "username does not match previous request");
|
||||||
|
error = -1;
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error = _git_ssh_authenticate_session(session, cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (no_callback) {
|
if (error < 0)
|
||||||
giterr_set(GITERR_SSH, "authentication required but no callback set");
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(t->cred);
|
|
||||||
|
|
||||||
if (_git_ssh_session_create(&session, s->socket) < 0)
|
|
||||||
goto on_error;
|
|
||||||
|
|
||||||
if (_git_ssh_authenticate_session(session, t->cred) < 0)
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
channel = libssh2_channel_open_session(session);
|
channel = libssh2_channel_open_session(session);
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
|
error = -1;
|
||||||
ssh_error(session, "Failed to open SSH channel");
|
ssh_error(session, "Failed to open SSH channel");
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
@ -474,6 +530,9 @@ static int _git_ssh_setup_conn(
|
|||||||
s->channel = channel;
|
s->channel = channel;
|
||||||
|
|
||||||
t->current_stream = s;
|
t->current_stream = s;
|
||||||
|
if (cred)
|
||||||
|
cred->free(cred);
|
||||||
|
|
||||||
git__free(host);
|
git__free(host);
|
||||||
git__free(port);
|
git__free(port);
|
||||||
git__free(path);
|
git__free(path);
|
||||||
@ -490,6 +549,9 @@ on_error:
|
|||||||
if (*stream)
|
if (*stream)
|
||||||
ssh_stream_free(*stream);
|
ssh_stream_free(*stream);
|
||||||
|
|
||||||
|
if (cred)
|
||||||
|
cred->free(cred);
|
||||||
|
|
||||||
git__free(host);
|
git__free(host);
|
||||||
git__free(port);
|
git__free(port);
|
||||||
git__free(user);
|
git__free(user);
|
||||||
@ -498,7 +560,7 @@ on_error:
|
|||||||
if (session)
|
if (session)
|
||||||
libssh2_session_free(session);
|
libssh2_session_free(session);
|
||||||
|
|
||||||
return -1;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ssh_uploadpack_ls(
|
static int ssh_uploadpack_ls(
|
||||||
@ -508,10 +570,7 @@ static int ssh_uploadpack_ls(
|
|||||||
{
|
{
|
||||||
const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
|
const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
|
||||||
|
|
||||||
if (_git_ssh_setup_conn(t, url, cmd, stream) < 0)
|
return _git_ssh_setup_conn(t, url, cmd, stream);
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ssh_uploadpack(
|
static int ssh_uploadpack(
|
||||||
@ -606,6 +665,53 @@ static void _ssh_free(git_smart_subtransport *subtransport)
|
|||||||
git__free(t->cmd_receivepack);
|
git__free(t->cmd_receivepack);
|
||||||
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, LIBSSH2_SESSION *session, const char *username)
|
||||||
|
{
|
||||||
|
const char *list, *ptr;
|
||||||
|
|
||||||
|
*out = 0;
|
||||||
|
|
||||||
|
list = libssh2_userauth_list(session, username, strlen(username));
|
||||||
|
|
||||||
|
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
|
||||||
|
if (list == NULL && !libssh2_userauth_authenticated(session))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
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, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int git_smart_subtransport_ssh(
|
int git_smart_subtransport_ssh(
|
||||||
|
|||||||
@ -12,6 +12,8 @@
|
|||||||
#define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git"
|
#define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git"
|
||||||
#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.git"
|
#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.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;
|
||||||
|
|
||||||
@ -222,8 +224,41 @@ void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
|
|||||||
|
|
||||||
g_options.remote_callbacks.credentials = cred_failure_cb;
|
g_options.remote_callbacks.credentials = cred_failure_cb;
|
||||||
|
|
||||||
/* TODO: this should expect -172. */
|
cl_git_fail_with(-172, git_clone(&g_repo, remote_url, "./foo", &g_options));
|
||||||
cl_git_fail_with(git_clone(&g_repo, remote_url, "./foo", &g_options), -1);
|
}
|
||||||
|
|
||||||
|
static int cred_count_calls_cb(git_cred **cred, const char *url, const char *user,
|
||||||
|
unsigned int allowed_types, void *data)
|
||||||
|
{
|
||||||
|
size_t *counter = (size_t *) data;
|
||||||
|
|
||||||
|
GIT_UNUSED(url); GIT_UNUSED(user); GIT_UNUSED(allowed_types);
|
||||||
|
|
||||||
|
if (allowed_types == GIT_CREDTYPE_USERNAME)
|
||||||
|
return git_cred_username_new(cred, "foo");
|
||||||
|
|
||||||
|
(*counter)++;
|
||||||
|
|
||||||
|
if (*counter == 3)
|
||||||
|
return GIT_EUSER;
|
||||||
|
|
||||||
|
return git_cred_userpass_plaintext_new(cred, "foo", "bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_online_clone__cred_callback_called_again_on_auth_failure(void)
|
||||||
|
{
|
||||||
|
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
|
||||||
|
const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
|
||||||
|
size_t counter = 0;
|
||||||
|
|
||||||
|
if (!remote_url || !remote_user)
|
||||||
|
clar__skip();
|
||||||
|
|
||||||
|
g_options.remote_callbacks.credentials = cred_count_calls_cb;
|
||||||
|
g_options.remote_callbacks.payload = &counter;
|
||||||
|
|
||||||
|
cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, remote_url, "./foo", &g_options));
|
||||||
|
cl_assert_equal_i(3, counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cred_default(
|
int cred_default(
|
||||||
@ -328,6 +363,36 @@ static int cred_cb(git_cred **cred, const char *url, const char *user_from_url,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_ssh_auth_methods(git_cred **cred, const char *url, const char *username_from_url,
|
||||||
|
unsigned int allowed_types, void *data)
|
||||||
|
{
|
||||||
|
int *with_user = (int *) data;
|
||||||
|
GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(data);
|
||||||
|
|
||||||
|
if (!*with_user)
|
||||||
|
cl_assert_equal_i(GIT_CREDTYPE_USERNAME, allowed_types);
|
||||||
|
else
|
||||||
|
cl_assert(!(allowed_types & GIT_CREDTYPE_USERNAME));
|
||||||
|
|
||||||
|
return GIT_EUSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_online_clone__ssh_auth_methods(void)
|
||||||
|
{
|
||||||
|
int with_user;
|
||||||
|
|
||||||
|
g_options.remote_callbacks.credentials = check_ssh_auth_methods;
|
||||||
|
g_options.remote_callbacks.payload = &with_user;
|
||||||
|
|
||||||
|
with_user = 0;
|
||||||
|
cl_git_fail_with(GIT_EUSER,
|
||||||
|
git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
|
||||||
|
|
||||||
|
with_user = 1;
|
||||||
|
cl_git_fail_with(GIT_EUSER,
|
||||||
|
git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
|
||||||
|
}
|
||||||
|
|
||||||
static int custom_remote_ssh_with_paths(
|
static int custom_remote_ssh_with_paths(
|
||||||
git_remote **out,
|
git_remote **out,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
@ -336,7 +401,6 @@ static int custom_remote_ssh_with_paths(
|
|||||||
void *payload)
|
void *payload)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
|
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
|
||||||
|
|
||||||
if ((error = git_remote_create(out, repo, name, url)) < 0)
|
if ((error = git_remote_create(out, repo, name, url)) < 0)
|
||||||
@ -381,3 +445,18 @@ void test_online_clone__ssh_with_paths(void)
|
|||||||
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
|
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cred_foo_bar(git_cred **cred, const char *url, const char *username_from_url,
|
||||||
|
unsigned int allowed_types, void *data)
|
||||||
|
|
||||||
|
{
|
||||||
|
GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(allowed_types); GIT_UNUSED(data);
|
||||||
|
|
||||||
|
return git_cred_userpass_plaintext_new(cred, "foo", "bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_online_clone__ssh_cannot_change_username(void)
|
||||||
|
{
|
||||||
|
g_options.remote_callbacks.credentials = cred_foo_bar;
|
||||||
|
|
||||||
|
cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
|
||||||
|
}
|
||||||
|
|||||||
@ -50,6 +50,15 @@ static int cred_acquire_cb(
|
|||||||
GIT_UNUSED(user_from_url);
|
GIT_UNUSED(user_from_url);
|
||||||
GIT_UNUSED(payload);
|
GIT_UNUSED(payload);
|
||||||
|
|
||||||
|
if (GIT_CREDTYPE_USERNAME & allowed_types) {
|
||||||
|
if (!_remote_user) {
|
||||||
|
printf("GITTEST_REMOTE_USER must be set\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return git_cred_username_new(cred, _remote_user);
|
||||||
|
}
|
||||||
|
|
||||||
if (GIT_CREDTYPE_DEFAULT & allowed_types) {
|
if (GIT_CREDTYPE_DEFAULT & allowed_types) {
|
||||||
if (!_remote_default) {
|
if (!_remote_default) {
|
||||||
printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
|
printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user