mirror of
https://git.proxmox.com/git/libgit2
synced 2025-07-06 01:49:24 +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
|
||||
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
|
||||
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_ELOCKED = -14, /**< Lock file prevented operation */
|
||||
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
|
||||
GIT_EAUTH = -16, /**< Authentication error */
|
||||
|
||||
GIT_PASSTHROUGH = -30, /**< Internal only */
|
||||
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
|
||||
|
@ -36,6 +36,14 @@ typedef enum {
|
||||
|
||||
/* git_cred_ssh_interactive */
|
||||
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;
|
||||
|
||||
/* The base structure for all credential types */
|
||||
@ -103,6 +111,12 @@ typedef struct git_cred_ssh_custom {
|
||||
/** A key for NTLM/Kerberos "default" credentials */
|
||||
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.
|
||||
*
|
||||
@ -204,6 +218,14 @@ GIT_EXTERN(int) git_cred_ssh_custom_new(
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -22,7 +22,13 @@ ctest -V . || exit $?
|
||||
# can do the push tests over it
|
||||
|
||||
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
|
||||
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
|
||||
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=""
|
||||
|
||||
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 &&
|
||||
git init --bare $HOME/_temp/test.git && # create an empty one
|
||||
./libgit2_clar -sonline::clone::ssh_with_paths
|
||||
|
@ -17,6 +17,40 @@ int git_cred_has_username(git_cred *cred)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static void username_free(struct git_cred *cred)
|
||||
{
|
||||
git__free(cred);
|
||||
}
|
||||
|
||||
int git_cred_ssh_key_new(
|
||||
git_cred **cred,
|
||||
const char *username,
|
||||
@ -263,3 +302,22 @@ int git_cred_default_new(git_cred **cred)
|
||||
*cred = c;
|
||||
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
|
||||
return -1;
|
||||
|
||||
if (GIT_CREDTYPE_USERNAME & allowed_types)
|
||||
return git_cred_username_new(cred, effective_username);
|
||||
|
||||
if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
|
||||
git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
|
||||
return -1;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "buffer.h"
|
||||
#include "netops.h"
|
||||
#include "smart.h"
|
||||
#include "cred.h"
|
||||
|
||||
#ifdef GIT_SSH
|
||||
|
||||
@ -41,6 +42,8 @@ typedef struct {
|
||||
char *cmd_receivepack;
|
||||
} 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)
|
||||
{
|
||||
char *ssherr;
|
||||
@ -354,6 +357,9 @@ static int _git_ssh_authenticate_session(
|
||||
}
|
||||
} 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) {
|
||||
ssh_error(session, "Failed to authenticate SSH session");
|
||||
return -1;
|
||||
@ -362,6 +368,43 @@ static int _git_ssh_authenticate_session(
|
||||
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(
|
||||
LIBSSH2_SESSION** session,
|
||||
gitno_socket socket)
|
||||
@ -402,8 +445,9 @@ static int _git_ssh_setup_conn(
|
||||
{
|
||||
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
|
||||
const char *default_port="22";
|
||||
int no_callback = 0;
|
||||
int auth_methods, error = 0;
|
||||
ssh_stream *s;
|
||||
git_cred *cred = NULL;
|
||||
LIBSSH2_SESSION* session=NULL;
|
||||
LIBSSH2_CHANNEL* channel=NULL;
|
||||
|
||||
@ -414,56 +458,68 @@ static int _git_ssh_setup_conn(
|
||||
s = (ssh_stream *)*stream;
|
||||
|
||||
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;
|
||||
} 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;
|
||||
port = git__strdup(default_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;
|
||||
|
||||
if (user && pass) {
|
||||
if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
|
||||
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_ssh_session_create(&session, s->socket)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (error == GIT_PASSTHROUGH)
|
||||
no_callback = 1;
|
||||
else if (error < 0)
|
||||
if ((error = list_auth_methods(&auth_methods, session, user)) < 0)
|
||||
goto on_error;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
error = _git_ssh_authenticate_session(session, cred);
|
||||
}
|
||||
|
||||
if (no_callback) {
|
||||
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)
|
||||
if (error < 0)
|
||||
goto on_error;
|
||||
|
||||
channel = libssh2_channel_open_session(session);
|
||||
if (!channel) {
|
||||
error = -1;
|
||||
ssh_error(session, "Failed to open SSH channel");
|
||||
goto on_error;
|
||||
}
|
||||
@ -474,6 +530,9 @@ static int _git_ssh_setup_conn(
|
||||
s->channel = channel;
|
||||
|
||||
t->current_stream = s;
|
||||
if (cred)
|
||||
cred->free(cred);
|
||||
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(path);
|
||||
@ -490,6 +549,9 @@ on_error:
|
||||
if (*stream)
|
||||
ssh_stream_free(*stream);
|
||||
|
||||
if (cred)
|
||||
cred->free(cred);
|
||||
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(user);
|
||||
@ -498,7 +560,7 @@ on_error:
|
||||
if (session)
|
||||
libssh2_session_free(session);
|
||||
|
||||
return -1;
|
||||
return error;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (_git_ssh_setup_conn(t, url, cmd, stream) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
return _git_ssh_setup_conn(t, url, cmd, stream);
|
||||
}
|
||||
|
||||
static int ssh_uploadpack(
|
||||
@ -606,6 +665,53 @@ static void _ssh_free(git_smart_subtransport *subtransport)
|
||||
git__free(t->cmd_receivepack);
|
||||
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
|
||||
|
||||
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_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_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;
|
||||
|
||||
/* TODO: this should expect -172. */
|
||||
cl_git_fail_with(git_clone(&g_repo, remote_url, "./foo", &g_options), -1);
|
||||
cl_git_fail_with(-172, git_clone(&g_repo, remote_url, "./foo", &g_options));
|
||||
}
|
||||
|
||||
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(
|
||||
@ -328,6 +363,36 @@ static int cred_cb(git_cred **cred, const char *url, const char *user_from_url,
|
||||
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(
|
||||
git_remote **out,
|
||||
git_repository *repo,
|
||||
@ -336,7 +401,6 @@ static int custom_remote_ssh_with_paths(
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
|
||||
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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(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 (!_remote_default) {
|
||||
printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
|
||||
|
Loading…
Reference in New Issue
Block a user