Unverified Commit 5b1667a9 by Edward Thomson Committed by GitHub

Merge pull request #6476 from libgit2/ethomson/v1.4.6

ssh: backport windows known_hosts fixes to v1.4
parents cd6f679a 08ed0881
...@@ -176,19 +176,25 @@ jobs: ...@@ -176,19 +176,25 @@ jobs:
- name: "Windows (amd64, Visual Studio)" - name: "Windows (amd64, Visual Studio)"
id: windows-amd64-vs id: windows-amd64-vs
os: windows-2019 os: windows-2019
setup-script: win32
env: env:
ARCH: amd64 ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019 CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
BUILD_TEMP: D:\Temp
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, Visual Studio)" - name: "Windows (x86, Visual Studio)"
id: windows-x86-vs id: windows-x86-vs
os: windows-2019 os: windows-2019
setup-script: win32
env: env:
ARCH: x86 ARCH: x86
CMAKE_GENERATOR: Visual Studio 16 2019 CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
BUILD_TEMP: D:\Temp
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true SKIP_NEGOTIATE_TESTS: true
- name: "Windows (amd64, mingw)" - name: "Windows (amd64, mingw)"
...@@ -251,6 +257,10 @@ jobs: ...@@ -251,6 +257,10 @@ jobs:
- name: Build and test - name: Build and test
run: | run: |
export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}" export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
export GITTEST_GITHUB_SSH_KEY="${{ secrets.GITTEST_GITHUB_SSH_KEY }}"
export GITTEST_GITHUB_SSH_PUBKEY="${{ secrets.GITTEST_GITHUB_SSH_PUBKEY }}"
export GITTEST_GITHUB_SSH_PASSPHRASE="${{ secrets.GITTEST_GITHUB_SSH_PASSPHRASE }}"
export GITTEST_GITHUB_SSH_REMOTE_HOSTKEY="${{ secrets.GITTEST_GITHUB_SSH_REMOTE_HOSTKEY }}"
if [ -n "${{ matrix.platform.container.name }}" ]; then if [ -n "${{ matrix.platform.container.name }}" ]; then
mkdir build mkdir build
......
...@@ -13,16 +13,30 @@ BUILD_PATH=${BUILD_PATH:=$PATH} ...@@ -13,16 +13,30 @@ BUILD_PATH=${BUILD_PATH:=$PATH}
CMAKE=$(which cmake) CMAKE=$(which cmake)
CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles} CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles}
indent() { sed "s/^/ /"; }
cygfullpath() {
result=$(echo "${1}" | tr \; \\n | while read -r element; do
if [ "${last}" != "" ]; then echo -n ":"; fi
echo -n $(cygpath "${element}")
last="${element}"
done)
if [ "${result}" = "" ]; then exit 1; fi
echo "${result}"
}
if [[ "$(uname -s)" == MINGW* ]]; then if [[ "$(uname -s)" == MINGW* ]]; then
BUILD_PATH=$(cygpath "$BUILD_PATH") BUILD_PATH=$(cygfullpath "${BUILD_PATH}")
fi fi
indent() { sed "s/^/ /"; }
echo "Source directory: ${SOURCE_DIR}" echo "Source directory: ${SOURCE_DIR}"
echo "Build directory: ${BUILD_DIR}" echo "Build directory: ${BUILD_DIR}"
echo "" echo ""
echo "Platform:"
uname -s | indent
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
echo "macOS version:" echo "macOS version:"
sw_vers | indent sw_vers | indent
...@@ -40,7 +54,7 @@ echo "Kernel version:" ...@@ -40,7 +54,7 @@ echo "Kernel version:"
uname -a 2>&1 | indent uname -a 2>&1 | indent
echo "CMake version:" echo "CMake version:"
env PATH="${BUILD_PATH}" "${CMAKE}" --version 2>&1 | indent env PATH="${BUILD_PATH}" "${CMAKE}" --version | head -1 2>&1 | indent
if test -n "${CC}"; then if test -n "${CC}"; then
echo "Compiler version:" echo "Compiler version:"
......
#!/bin/sh
set -ex
echo "##############################################################################"
echo "## Downloading libssh2"
echo "##############################################################################"
BUILD_TEMP=${BUILD_TEMP:=$TEMP}
BUILD_TEMP=$(cygpath $BUILD_TEMP)
case "$ARCH" in
amd64)
LIBSSH2_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-02-01/libssh2-20230201-amd64.zip";;
x86)
LIBSSH2_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-02-01-v2/libssh2-20230201-x86.zip";;
esac
if [ -z "$LIBSSH2_URI" ]; then
echo "No URL"
exit 1
fi
mkdir -p "$BUILD_TEMP"
curl -s -L "$LIBSSH2_URI" -o "$BUILD_TEMP"/libssh2-"$ARCH".zip
unzip -q "$BUILD_TEMP"/libssh2-"$ARCH".zip -d "$BUILD_TEMP"
...@@ -13,6 +13,8 @@ fi ...@@ -13,6 +13,8 @@ fi
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )} SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd) BUILD_DIR=$(pwd)
BUILD_PATH=${BUILD_PATH:=$PATH}
CTEST=$(which ctest)
TMPDIR=${TMPDIR:-/tmp} TMPDIR=${TMPDIR:-/tmp}
USER=${USER:-$(whoami)} USER=${USER:-$(whoami)}
...@@ -52,7 +54,11 @@ run_test() { ...@@ -52,7 +54,11 @@ run_test() {
RETURN_CODE=0 RETURN_CODE=0
CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml" ctest -V -R "^${1}$" || RETURN_CODE=$? && true (
export PATH="${BUILD_PATH}"
export CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml"
"${CTEST}" -V -R "^${1}$"
) || RETURN_CODE=$? && true
if [ "$RETURN_CODE" -eq 0 ]; then if [ "$RETURN_CODE" -eq 0 ]; then
FAILED=0 FAILED=0
...@@ -73,9 +79,31 @@ run_test() { ...@@ -73,9 +79,31 @@ run_test() {
fi fi
} }
indent() { sed "s/^/ /"; }
cygfullpath() {
result=$(echo "${1}" | tr \; \\n | while read -r element; do
if [ "${last}" != "" ]; then echo -n ":"; fi
echo -n $(cygpath "${element}")
last="${element}"
done)
if [ "${result}" = "" ]; then exit 1; fi
echo "${result}"
}
if [[ "$(uname -s)" == MINGW* ]]; then
BUILD_PATH=$(cygfullpath "$BUILD_PATH")
fi
# Configure the test environment; run them early so that we're certain # Configure the test environment; run them early so that we're certain
# that they're started by the time we need them. # that they're started by the time we need them.
echo "CTest version:"
env PATH="${BUILD_PATH}" "${CTEST}" --version | head -1 2>&1 | indent
echo ""
echo "##############################################################################" echo "##############################################################################"
echo "## Configuring test environment" echo "## Configuring test environment"
echo "##############################################################################" echo "##############################################################################"
...@@ -348,7 +376,7 @@ if [ -z "$SKIP_FUZZERS" ]; then ...@@ -348,7 +376,7 @@ if [ -z "$SKIP_FUZZERS" ]; then
echo "## Running fuzzers" echo "## Running fuzzers"
echo "##############################################################################" echo "##############################################################################"
ctest -V -R 'fuzzer' env PATH="${BUILD_PATH}" "${CTEST}" -V -R 'fuzzer'
fi fi
cleanup cleanup
......
...@@ -75,7 +75,7 @@ out: ...@@ -75,7 +75,7 @@ out:
} }
#endif #endif
static int git_sysdir_guess_global_dirs(git_str *out) static int git_sysdir_guess_home_dirs(git_str *out)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
return git_win32__find_global_dirs(out); return git_win32__find_global_dirs(out);
...@@ -114,6 +114,11 @@ static int git_sysdir_guess_global_dirs(git_str *out) ...@@ -114,6 +114,11 @@ static int git_sysdir_guess_global_dirs(git_str *out)
#endif #endif
} }
static int git_sysdir_guess_global_dirs(git_str *out)
{
return git_sysdir_guess_home_dirs(out);
}
static int git_sysdir_guess_xdg_dirs(git_str *out) static int git_sysdir_guess_xdg_dirs(git_str *out)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
...@@ -171,6 +176,7 @@ static struct git_sysdir__dir git_sysdir__dirs[] = { ...@@ -171,6 +176,7 @@ static struct git_sysdir__dir git_sysdir__dirs[] = {
{ GIT_STR_INIT, git_sysdir_guess_xdg_dirs }, { GIT_STR_INIT, git_sysdir_guess_xdg_dirs },
{ GIT_STR_INIT, git_sysdir_guess_programdata_dirs }, { GIT_STR_INIT, git_sysdir_guess_programdata_dirs },
{ GIT_STR_INIT, git_sysdir_guess_template_dirs }, { GIT_STR_INIT, git_sysdir_guess_template_dirs },
{ GIT_STR_INIT, git_sysdir_guess_home_dirs }
}; };
static void git_sysdir_global_shutdown(void) static void git_sysdir_global_shutdown(void)
...@@ -350,6 +356,12 @@ int git_sysdir_find_template_dir(git_str *path) ...@@ -350,6 +356,12 @@ int git_sysdir_find_template_dir(git_str *path)
path, NULL, GIT_SYSDIR_TEMPLATE, "template"); path, NULL, GIT_SYSDIR_TEMPLATE, "template");
} }
int git_sysdir_find_homedir(git_str *path)
{
return git_sysdir_find_in_dirlist(
path, NULL, GIT_SYSDIR_HOME, "home directory");
}
int git_sysdir_expand_global_file(git_str *path, const char *filename) int git_sysdir_expand_global_file(git_str *path, const char *filename)
{ {
int error; int error;
...@@ -361,3 +373,15 @@ int git_sysdir_expand_global_file(git_str *path, const char *filename) ...@@ -361,3 +373,15 @@ int git_sysdir_expand_global_file(git_str *path, const char *filename)
return error; return error;
} }
int git_sysdir_expand_homedir_file(git_str *path, const char *filename)
{
int error;
if ((error = git_sysdir_find_homedir(path)) == 0) {
if (filename)
error = git_str_joinpath(path, path->ptr, filename);
}
return error;
}
...@@ -57,10 +57,22 @@ extern int git_sysdir_find_programdata_file(git_str *path, const char *filename) ...@@ -57,10 +57,22 @@ extern int git_sysdir_find_programdata_file(git_str *path, const char *filename)
extern int git_sysdir_find_template_dir(git_str *path); extern int git_sysdir_find_template_dir(git_str *path);
/** /**
* Expand the name of a "global" file (i.e. one in a user's home * Find the home directory. On Windows, this will look at the `HOME`,
* directory). Unlike `find_global_file` (above), this makes no * `HOMEPATH`, and `USERPROFILE` environment variables (in that order)
* attempt to check for the existence of the file, and is useful if * and return the first path that is set and exists. On other systems,
* you want the full path regardless of existence. * this will simply return the contents of the `HOME` environment variable.
*
* @param path buffer to write the full path into
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
extern int git_sysdir_find_homedir(git_str *path);
/**
* Expand the name of a "global" file -- by default inside the user's
* home directory, but can be overridden by the user configuration.
* Unlike `find_global_file` (above), this makes no attempt to check
* for the existence of the file, and is useful if you want the full
* path regardless of existence.
* *
* @param path buffer to write the full path into * @param path buffer to write the full path into
* @param filename name of file in the home directory * @param filename name of file in the home directory
...@@ -68,13 +80,25 @@ extern int git_sysdir_find_template_dir(git_str *path); ...@@ -68,13 +80,25 @@ extern int git_sysdir_find_template_dir(git_str *path);
*/ */
extern int git_sysdir_expand_global_file(git_str *path, const char *filename); extern int git_sysdir_expand_global_file(git_str *path, const char *filename);
/**
* Expand the name of a file in the user's home directory. This
* function makes no attempt to check for the existence of the file,
* and is useful if you want the full path regardless of existence.
*
* @param path buffer to write the full path into
* @param filename name of file in the home directory
* @return 0 on success or -1 on error
*/
extern int git_sysdir_expand_homedir_file(git_str *path, const char *filename);
typedef enum { typedef enum {
GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_SYSTEM = 0,
GIT_SYSDIR_GLOBAL = 1, GIT_SYSDIR_GLOBAL = 1,
GIT_SYSDIR_XDG = 2, GIT_SYSDIR_XDG = 2,
GIT_SYSDIR_PROGRAMDATA = 3, GIT_SYSDIR_PROGRAMDATA = 3,
GIT_SYSDIR_TEMPLATE = 4, GIT_SYSDIR_TEMPLATE = 4,
GIT_SYSDIR__MAX = 5 GIT_SYSDIR_HOME = 5,
GIT_SYSDIR__MAX = 6
} git_sysdir_t; } git_sysdir_t;
/** /**
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "netops.h" #include "netops.h"
#include "smart.h" #include "smart.h"
#include "streams/socket.h" #include "streams/socket.h"
#include "sysdir.h"
#include "git2/credential.h" #include "git2/credential.h"
#include "git2/sys/credential.h" #include "git2/sys/credential.h"
...@@ -421,7 +422,8 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char * ...@@ -421,7 +422,8 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
return 0; return 0;
} }
#define KNOWN_HOSTS_FILE ".ssh/known_hosts" #define SSH_DIR ".ssh"
#define KNOWN_HOSTS_FILE "known_hosts"
/* /*
* Load the known_hosts file. * Load the known_hosts file.
...@@ -430,16 +432,14 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char * ...@@ -430,16 +432,14 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
*/ */
static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session) static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session)
{ {
git_str path = GIT_STR_INIT, home = GIT_STR_INIT; git_str path = GIT_STR_INIT, sshdir = GIT_STR_INIT;
LIBSSH2_KNOWNHOSTS *known_hosts = NULL; LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
int error; int error;
GIT_ASSERT_ARG(hosts); GIT_ASSERT_ARG(hosts);
if ((error = git__getenv(&home, "HOME")) < 0) if ((error = git_sysdir_expand_homedir_file(&sshdir, SSH_DIR)) < 0 ||
return error; (error = git_str_joinpath(&path, git_str_cstr(&sshdir), KNOWN_HOSTS_FILE)) < 0)
if ((error = git_str_joinpath(&path, git_str_cstr(&home), KNOWN_HOSTS_FILE)) < 0)
goto out; goto out;
if ((known_hosts = libssh2_knownhost_init(session)) == NULL) { if ((known_hosts = libssh2_knownhost_init(session)) == NULL) {
...@@ -461,34 +461,32 @@ static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session ...@@ -461,34 +461,32 @@ static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session
out: out:
*hosts = known_hosts; *hosts = known_hosts;
git_str_clear(&home); git_str_dispose(&sshdir);
git_str_clear(&path); git_str_dispose(&path);
return error; return error;
} }
static const char *hostkey_type_to_string(int type) static void add_hostkey_pref_if_avail(
LIBSSH2_KNOWNHOSTS *known_hosts,
const char *hostname,
int port,
git_str *prefs,
int type,
const char *type_name)
{ {
switch (type) { struct libssh2_knownhost *host = NULL;
case LIBSSH2_KNOWNHOST_KEY_SSHRSA: const char key = '\0';
return "ssh-rsa"; int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | type;
case LIBSSH2_KNOWNHOST_KEY_SSHDSS: int error;
return "ssh-dss";
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
return "ecdsa-sha2-nistp256";
case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
return "ecdsa-sha2-nistp384";
case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
return "ecdsa-sha2-nistp521";
#endif
#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
case LIBSSH2_KNOWNHOST_KEY_ED25519:
return "ssh-ed25519";
#endif
}
return NULL; error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, mask, &host);
if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
if (git_str_len(prefs) > 0) {
git_str_putc(prefs, ',');
}
git_str_puts(prefs, type_name);
}
} }
/* /*
...@@ -496,27 +494,27 @@ static const char *hostkey_type_to_string(int type) ...@@ -496,27 +494,27 @@ static const char *hostkey_type_to_string(int type)
* look it up with a nonsense key and using that mismatch to figure out what key * look it up with a nonsense key and using that mismatch to figure out what key
* we do have stored for the host. * we do have stored for the host.
* *
* Returns the string to pass to libssh2_session_method_pref or NULL if we were * Populates prefs with the string to pass to libssh2_session_method_pref.
* unable to find anything or an error happened.
*/ */
static const char *find_hostkey_preference(LIBSSH2_KNOWNHOSTS *known_hosts, const char *hostname, int port) static void find_hostkey_preference(
LIBSSH2_KNOWNHOSTS *known_hosts,
const char *hostname,
int port,
git_str *prefs)
{ {
struct libssh2_knownhost *host = NULL;
/* Specify no key type so we don't filter on that */
int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
const char key = '\0';
int error;
/* /*
* In case of mismatch, we can find the type of key from known_hosts in * The order here is important as it indicates the priority of what will
* the returned host's information as it means that an entry was found * be preferred.
* but our nonsense key obviously didn't match.
*/ */
error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, type, &host); #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ED25519, "ssh-ed25519");
return hostkey_type_to_string(host->typemask & LIBSSH2_KNOWNHOST_KEY_MASK); #endif
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
return NULL; add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_256, "ecdsa-sha2-nistp256");
add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_384, "ecdsa-sha2-nistp384");
add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_521, "ecdsa-sha2-nistp521");
#endif
add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_SSHRSA, "ssh-rsa");
} }
static int _git_ssh_session_create( static int _git_ssh_session_create(
...@@ -526,11 +524,11 @@ static int _git_ssh_session_create( ...@@ -526,11 +524,11 @@ static int _git_ssh_session_create(
int port, int port,
git_stream *io) git_stream *io)
{ {
int rc = 0; git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
LIBSSH2_SESSION *s; LIBSSH2_SESSION *s;
LIBSSH2_KNOWNHOSTS *known_hosts; LIBSSH2_KNOWNHOSTS *known_hosts;
git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent); git_str prefs = GIT_STR_INIT;
const char *keytype = NULL; int rc = 0;
GIT_ASSERT_ARG(session); GIT_ASSERT_ARG(session);
GIT_ASSERT_ARG(hosts); GIT_ASSERT_ARG(hosts);
...@@ -547,16 +545,17 @@ static int _git_ssh_session_create( ...@@ -547,16 +545,17 @@ static int _git_ssh_session_create(
return -1; return -1;
} }
if ((keytype = find_hostkey_preference(known_hosts, hostname, port)) != NULL) { find_hostkey_preference(known_hosts, hostname, port, &prefs);
if (git_str_len(&prefs) > 0) {
do { do {
rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, keytype); rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, git_str_cstr(&prefs));
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (rc != LIBSSH2_ERROR_NONE) { if (rc != LIBSSH2_ERROR_NONE) {
ssh_error(s, "failed to set hostkey preference"); ssh_error(s, "failed to set hostkey preference");
goto on_error; goto on_error;
} }
} }
git_str_dispose(&prefs);
do { do {
rc = libssh2_session_handshake(s, socket->s); rc = libssh2_session_handshake(s, socket->s);
...@@ -753,7 +752,7 @@ static int check_certificate( ...@@ -753,7 +752,7 @@ static int check_certificate(
if (error == GIT_PASSTHROUGH) { if (error == GIT_PASSTHROUGH) {
error = git_error_state_restore(&previous_error); error = git_error_state_restore(&previous_error);
} else if (error < 0 && !git_error_last()) { } else if (error < 0 && !git_error_last()) {
git_error_set(GIT_ERROR_NET, "user canceled hostkey check"); git_error_set(GIT_ERROR_NET, "unknown remote host key");
} }
git_error_state_free(&previous_error); git_error_state_free(&previous_error);
...@@ -1009,7 +1008,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use ...@@ -1009,7 +1008,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */ /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
if (list == NULL && !libssh2_userauth_authenticated(session)) { if (list == NULL && !libssh2_userauth_authenticated(session)) {
ssh_error(session, "Failed to retrieve list of SSH authentication methods"); ssh_error(session, "remote rejected authentication");
return GIT_EAUTH; return GIT_EAUTH;
} }
......
...@@ -70,7 +70,7 @@ endfunction(ADD_CLAR_TEST) ...@@ -70,7 +70,7 @@ endfunction(ADD_CLAR_TEST)
add_clar_test(offline -v -xonline) add_clar_test(offline -v -xonline)
add_clar_test(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root) add_clar_test(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
add_clar_test(online -v -sonline -xonline::customcert -xonline::clone::ssh_auth_methods) add_clar_test(online -v -sonline -xonline::customcert)
add_clar_test(online_customcert -v -sonline::customcert) add_clar_test(online_customcert -v -sonline::customcert)
add_clar_test(gitdaemon -v -sonline::push) add_clar_test(gitdaemon -v -sonline::push)
add_clar_test(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh -sonline::clone::ssh_auth_methods) add_clar_test(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh -sonline::clone::ssh_auth_methods)
......
...@@ -548,31 +548,61 @@ void clar__assert_equal_file( ...@@ -548,31 +548,61 @@ void clar__assert_equal_file(
(size_t)expected_bytes, (size_t)total_bytes); (size_t)expected_bytes, (size_t)total_bytes);
} }
static git_buf _cl_restore_home = GIT_BUF_INIT; static git_buf _cl_restore_homedir = GIT_BUF_INIT;
void cl_fake_home_cleanup(void *payload) void cl_fake_homedir_cleanup(void *payload)
{ {
GIT_UNUSED(payload); GIT_UNUSED(payload);
if (_cl_restore_home.ptr) { if (_cl_restore_homedir.ptr) {
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, _cl_restore_home.ptr)); GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, _cl_restore_homedir.ptr));
git_buf_dispose(&_cl_restore_home); git_buf_dispose(&_cl_restore_homedir);
} }
} }
void cl_fake_home(void) void cl_fake_homedir(void)
{ {
git_str path = GIT_STR_INIT; git_str path = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_home)); GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_homedir));
cl_set_cleanup(cl_fake_home_cleanup, NULL); cl_set_cleanup(cl_fake_homedir_cleanup, NULL);
if (!git_fs_path_exists("home")) if (!git_fs_path_exists("homedir"))
cl_must_pass(p_mkdir("home", 0777)); cl_must_pass(p_mkdir("homedir", 0777));
cl_git_pass(git_fs_path_prettify(&path, "home", NULL)); cl_git_pass(git_fs_path_prettify(&path, "homedir", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_str_dispose(&path);
}
static git_buf _cl_restore_globalconfig = GIT_BUF_INIT;
void cl_fake_globalconfig_cleanup(void *payload)
{
GIT_UNUSED(payload);
if (_cl_restore_globalconfig.ptr) {
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, _cl_restore_globalconfig.ptr));
git_buf_dispose(&_cl_restore_globalconfig);
}
}
void cl_fake_globalconfig(void)
{
git_str path = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_globalconfig));
cl_set_cleanup(cl_fake_globalconfig_cleanup, NULL);
if (!git_fs_path_exists("globalconfig"))
cl_must_pass(p_mkdir("globalconfig", 0777));
cl_git_pass(git_fs_path_prettify(&path, "globalconfig", NULL));
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_str_dispose(&path); git_str_dispose(&path);
......
...@@ -213,13 +213,23 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg); ...@@ -213,13 +213,23 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg);
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value); void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
/* set up a fake "home" directory and set libgit2 GLOBAL search path. /* set up a fake "home" directory
* *
* automatically configures cleanup function to restore the regular search * automatically configures cleanup function to restore the regular search
* path, although you can call it explicitly if you wish (with NULL). * path, although you can call it explicitly if you wish (with NULL).
*/ */
void cl_fake_home(void); void cl_fake_homedir(void);
void cl_fake_home_cleanup(void *); void cl_fake_homedir_cleanup(void *);
/*
* set up a fake directory for the libgit2 GLOBAL search path.
*
* automatically configures cleanup function to restore the regular search
* path, although you can call it explicitly if you wish (with NULL).
*/
void cl_fake_globalconfig(void);
void cl_fake_globalconfig_cleanup(void *);
void cl_sandbox_set_search_path_defaults(void); void cl_sandbox_set_search_path_defaults(void);
void cl_sandbox_disable_ownership_validation(void); void cl_sandbox_disable_ownership_validation(void);
......
...@@ -290,10 +290,10 @@ void test_ignore_path__expand_tilde_to_homedir(void) ...@@ -290,10 +290,10 @@ void test_ignore_path__expand_tilde_to_homedir(void)
assert_is_ignored(false, "example.global_with_tilde"); assert_is_ignored(false, "example.global_with_tilde");
cl_fake_home(); cl_fake_globalconfig();
/* construct fake home with fake global excludes */ /* construct fake home with fake global excludes */
cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n"); cl_git_mkfile("globalconfig/globalexclude", "# found me\n*.global_with_tilde\n");
cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude")); cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude"));
...@@ -305,7 +305,7 @@ void test_ignore_path__expand_tilde_to_homedir(void) ...@@ -305,7 +305,7 @@ void test_ignore_path__expand_tilde_to_homedir(void)
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
cl_fake_home_cleanup(NULL); cl_fake_globalconfig_cleanup(NULL);
git_attr_cache_flush(g_repo); /* must reset to pick up change */ git_attr_cache_flush(g_repo); /* must reset to pick up change */
......
...@@ -385,8 +385,8 @@ void test_ignore_status__leading_slash_ignores(void) ...@@ -385,8 +385,8 @@ void test_ignore_status__leading_slash_ignores(void)
make_test_data(test_repo_1, test_files_1); make_test_data(test_repo_1, test_files_1);
cl_fake_home(); cl_fake_globalconfig();
cl_git_mkfile("home/.gitignore", "/ignore_me\n"); cl_git_mkfile("globalconfig/.gitignore", "/ignore_me\n");
{ {
git_config *cfg; git_config *cfg;
cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_repository_config(&cfg, g_repo));
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "remote.h" #include "remote.h"
#include "futils.h" #include "futils.h"
#include "refs.h" #include "refs.h"
#include "sysdir.h"
#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository" #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository" #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
...@@ -35,6 +36,11 @@ static char *_remote_expectcontinue = NULL; ...@@ -35,6 +36,11 @@ static char *_remote_expectcontinue = NULL;
static char *_remote_redirect_initial = NULL; static char *_remote_redirect_initial = NULL;
static char *_remote_redirect_subsequent = NULL; static char *_remote_redirect_subsequent = NULL;
static char *_github_ssh_pubkey = NULL;
static char *_github_ssh_privkey = NULL;
static char *_github_ssh_passphrase = NULL;
static char *_github_ssh_remotehostkey = NULL;
static int _orig_proxies_need_reset = 0; static int _orig_proxies_need_reset = 0;
static char *_orig_http_proxy = NULL; static char *_orig_http_proxy = NULL;
static char *_orig_https_proxy = NULL; static char *_orig_https_proxy = NULL;
...@@ -83,6 +89,11 @@ void test_online_clone__initialize(void) ...@@ -83,6 +89,11 @@ void test_online_clone__initialize(void)
_remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL"); _remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
_remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT"); _remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
_github_ssh_pubkey = cl_getenv("GITTEST_GITHUB_SSH_PUBKEY");
_github_ssh_privkey = cl_getenv("GITTEST_GITHUB_SSH_KEY");
_github_ssh_passphrase = cl_getenv("GITTEST_GITHUB_SSH_PASSPHRASE");
_github_ssh_remotehostkey = cl_getenv("GITTEST_GITHUB_SSH_REMOTE_HOSTKEY");
if (_remote_expectcontinue) if (_remote_expectcontinue)
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1); git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
...@@ -116,6 +127,11 @@ void test_online_clone__cleanup(void) ...@@ -116,6 +127,11 @@ void test_online_clone__cleanup(void)
git__free(_remote_redirect_initial); git__free(_remote_redirect_initial);
git__free(_remote_redirect_subsequent); git__free(_remote_redirect_subsequent);
git__free(_github_ssh_pubkey);
git__free(_github_ssh_privkey);
git__free(_github_ssh_passphrase);
git__free(_github_ssh_remotehostkey);
if (_orig_proxies_need_reset) { if (_orig_proxies_need_reset) {
cl_setenv("HTTP_PROXY", _orig_http_proxy); cl_setenv("HTTP_PROXY", _orig_http_proxy);
cl_setenv("HTTPS_PROXY", _orig_https_proxy); cl_setenv("HTTPS_PROXY", _orig_https_proxy);
...@@ -537,6 +553,68 @@ static int check_ssh_auth_methods(git_credential **cred, const char *url, const ...@@ -537,6 +553,68 @@ static int check_ssh_auth_methods(git_credential **cred, const char *url, const
return GIT_EUSER; return GIT_EUSER;
} }
static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(cert);
GIT_UNUSED(valid);
GIT_UNUSED(payload);
cl_assert_equal_s("github.com", host);
return 0;
}
static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(cert);
GIT_UNUSED(valid);
GIT_UNUSED(host);
GIT_UNUSED(payload);
return GIT_ECERTIFICATE;
}
static int github_credentials(
git_credential **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(data);
if ((allowed_types & GIT_CREDENTIAL_USERNAME) != 0) {
return git_credential_username_new(cred, "git");
}
cl_assert((allowed_types & GIT_CREDENTIAL_SSH_KEY) != 0);
return git_credential_ssh_key_memory_new(cred,
"git",
_github_ssh_pubkey,
_github_ssh_privkey,
_github_ssh_passphrase);
}
void test_online_clone__ssh_github(void)
{
#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
clar__skip();
#endif
if (!_github_ssh_pubkey || !_github_ssh_privkey)
clar__skip();
cl_fake_homedir();
g_options.fetch_opts.callbacks.credentials = github_credentials;
g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
}
void test_online_clone__ssh_auth_methods(void) void test_online_clone__ssh_auth_methods(void)
{ {
int with_user; int with_user;
...@@ -546,7 +624,7 @@ void test_online_clone__ssh_auth_methods(void) ...@@ -546,7 +624,7 @@ void test_online_clone__ssh_auth_methods(void)
#endif #endif
g_options.fetch_opts.callbacks.credentials = check_ssh_auth_methods; g_options.fetch_opts.callbacks.credentials = check_ssh_auth_methods;
g_options.fetch_opts.callbacks.payload = &with_user; g_options.fetch_opts.callbacks.payload = &with_user;
g_options.fetch_opts.callbacks.certificate_check = NULL; g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
with_user = 0; with_user = 0;
cl_git_fail_with(GIT_EUSER, cl_git_fail_with(GIT_EUSER,
...@@ -557,6 +635,71 @@ void test_online_clone__ssh_auth_methods(void) ...@@ -557,6 +635,71 @@ void test_online_clone__ssh_auth_methods(void)
git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options)); git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
} }
/*
* Ensure that the certificate check callback is still called, and
* can accept a host key that is not in the known hosts file.
*/
void test_online_clone__ssh_certcheck_accepts_unknown(void)
{
#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
clar__skip();
#endif
if (!_github_ssh_pubkey || !_github_ssh_privkey)
clar__skip();
cl_fake_homedir();
g_options.fetch_opts.callbacks.credentials = github_credentials;
/* Ensure we fail without the certificate check */
cl_git_fail_with(GIT_ECERTIFICATE,
git_clone(&g_repo, SSH_REPO_URL, "./foo", NULL));
/* Set the callback to accept the certificate */
g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
}
/*
* Ensure that the known hosts file is read and the certificate check
* callback is still called after that.
*/
void test_online_clone__ssh_certcheck_override_knownhosts(void)
{
git_str knownhostsfile = GIT_STR_INIT;
#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
clar__skip();
#endif
if (!_github_ssh_pubkey || !_github_ssh_privkey || !_github_ssh_remotehostkey)
clar__skip();
g_options.fetch_opts.callbacks.credentials = github_credentials;
cl_fake_homedir();
cl_git_pass(git_sysdir_find_homedir(&knownhostsfile));
cl_git_pass(git_str_joinpath(&knownhostsfile, knownhostsfile.ptr, ".ssh"));
cl_git_pass(p_mkdir(knownhostsfile.ptr, 0777));
cl_git_pass(git_str_joinpath(&knownhostsfile, knownhostsfile.ptr, "known_hosts"));
cl_git_rewritefile(knownhostsfile.ptr, _github_ssh_remotehostkey);
/* Ensure we succeed without the certificate check */
cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
git_repository_free(g_repo);
g_repo = NULL;
/* Set the callback to reject the certificate */
g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, SSH_REPO_URL, "./bar", &g_options));
git_str_dispose(&knownhostsfile);
}
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,
...@@ -729,16 +872,6 @@ void test_online_clone__ssh_memory_auth(void) ...@@ -729,16 +872,6 @@ void test_online_clone__ssh_memory_auth(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 fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(cert);
GIT_UNUSED(valid);
GIT_UNUSED(host);
GIT_UNUSED(payload);
return GIT_ECERTIFICATE;
}
void test_online_clone__certificate_invalid(void) void test_online_clone__certificate_invalid(void)
{ {
g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check; g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
...@@ -752,17 +885,6 @@ void test_online_clone__certificate_invalid(void) ...@@ -752,17 +885,6 @@ void test_online_clone__certificate_invalid(void)
#endif #endif
} }
static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(cert);
GIT_UNUSED(valid);
GIT_UNUSED(payload);
cl_assert_equal_s("github.com", host);
return 0;
}
void test_online_clone__certificate_valid(void) void test_online_clone__certificate_valid(void)
{ {
g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check; g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
......
...@@ -132,7 +132,7 @@ static void assert_global_config_match(const char *config, const char *expected) ...@@ -132,7 +132,7 @@ static void assert_global_config_match(const char *config, const char *expected)
void test_remote_httpproxy__config_overrides_detached_remote(void) void test_remote_httpproxy__config_overrides_detached_remote(void)
{ {
cl_fake_home(); cl_fake_globalconfig();
assert_global_config_match(NULL, NULL); assert_global_config_match(NULL, NULL);
assert_global_config_match("http.proxy", "http://localhost:1/"); assert_global_config_match("http.proxy", "http://localhost:1/");
......
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