mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-09 20:08:50 +00:00
Merge pull request #2497 from ethomson/kerberos3
SPNEGO authentication via GSSAPI
This commit is contained in:
commit
e1c4429038
@ -36,6 +36,7 @@ OPTION( ANDROID "Build for android NDK" OFF )
|
||||
|
||||
OPTION( USE_ICONV "Link with and use iconv library" OFF )
|
||||
OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
|
||||
OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
|
||||
OPTION( VALGRIND "Configure build for valgrind" OFF )
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
@ -208,6 +209,14 @@ IF (LIBSSH2_FOUND)
|
||||
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
# Optional external dependency: libgssapi
|
||||
IF (USE_GSSAPI)
|
||||
FIND_PACKAGE(GSSAPI)
|
||||
ENDIF()
|
||||
IF (GSSAPI_FOUND)
|
||||
ADD_DEFINITIONS(-DGIT_GSSAPI)
|
||||
ENDIF()
|
||||
|
||||
# Optional external dependency: iconv
|
||||
IF (USE_ICONV)
|
||||
FIND_PACKAGE(Iconv)
|
||||
@ -387,6 +396,7 @@ ENDIF()
|
||||
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
|
||||
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
|
||||
TARGET_OS_LIBRARIES(git2)
|
||||
|
||||
@ -453,6 +463,7 @@ IF (BUILD_CLAR)
|
||||
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
|
||||
TARGET_OS_LIBRARIES(libgit2_clar)
|
||||
MSVC_SPLIT_SOURCES(libgit2_clar)
|
||||
|
324
cmake/Modules/FindGSSAPI.cmake
Normal file
324
cmake/Modules/FindGSSAPI.cmake
Normal file
@ -0,0 +1,324 @@
|
||||
# - Try to find GSSAPI
|
||||
# Once done this will define
|
||||
#
|
||||
# KRB5_CONFIG - Path to krb5-config
|
||||
# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI
|
||||
#
|
||||
# Read-Only variables:
|
||||
# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found
|
||||
# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found
|
||||
# GSSAPI_FOUND - system has GSSAPI
|
||||
# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
|
||||
# GSSAPI_LIBRARIES - Link these to use GSSAPI
|
||||
# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI
|
||||
#
|
||||
#=============================================================================
|
||||
# Copyright (c) 2013 Andreas Schneider <asn@cryptomilk.org>
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
#
|
||||
|
||||
find_path(GSSAPI_ROOT_DIR
|
||||
NAMES
|
||||
include/gssapi.h
|
||||
include/gssapi/gssapi.h
|
||||
HINTS
|
||||
${_GSSAPI_ROOT_HINTS}
|
||||
PATHS
|
||||
${_GSSAPI_ROOT_PATHS}
|
||||
)
|
||||
mark_as_advanced(GSSAPI_ROOT_DIR)
|
||||
|
||||
if (UNIX)
|
||||
find_program(KRB5_CONFIG
|
||||
NAMES
|
||||
krb5-config
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/bin
|
||||
/opt/local/bin)
|
||||
mark_as_advanced(KRB5_CONFIG)
|
||||
|
||||
if (KRB5_CONFIG)
|
||||
# Check if we have MIT KRB5
|
||||
execute_process(
|
||||
COMMAND
|
||||
${KRB5_CONFIG} --vendor
|
||||
RESULT_VARIABLE
|
||||
_GSSAPI_VENDOR_RESULT
|
||||
OUTPUT_VARIABLE
|
||||
_GSSAPI_VENDOR_STRING)
|
||||
|
||||
if (_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*")
|
||||
set(GSSAPI_FLAVOR_MIT TRUE)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND
|
||||
${KRB5_CONFIG} --libs gssapi
|
||||
RESULT_VARIABLE
|
||||
_GSSAPI_LIBS_RESULT
|
||||
OUTPUT_VARIABLE
|
||||
_GSSAPI_LIBS_STRING)
|
||||
|
||||
if (_GSSAPI_LIBS_STRING MATCHES ".*roken.*")
|
||||
set(GSSAPI_FLAVOR_HEIMDAL TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Get the include dir
|
||||
execute_process(
|
||||
COMMAND
|
||||
${KRB5_CONFIG} --cflags gssapi
|
||||
RESULT_VARIABLE
|
||||
_GSSAPI_INCLUDE_RESULT
|
||||
OUTPUT_VARIABLE
|
||||
_GSSAPI_INCLUDE_STRING)
|
||||
string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}")
|
||||
string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}")
|
||||
endif()
|
||||
|
||||
if (NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL)
|
||||
# Check for HEIMDAL
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_GSSAPI heimdal-gssapi)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
if (_GSSAPI_FOUND)
|
||||
set(GSSAPI_FLAVOR_HEIMDAL TRUE)
|
||||
else()
|
||||
find_path(_GSSAPI_ROKEN
|
||||
NAMES
|
||||
roken.h
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/include
|
||||
${_GSSAPI_INCLUDEDIR})
|
||||
if (_GSSAPI_ROKEN)
|
||||
set(GSSAPI_FLAVOR_HEIMDAL TRUE)
|
||||
endif()
|
||||
endif ()
|
||||
endif()
|
||||
endif (UNIX)
|
||||
|
||||
find_path(GSSAPI_INCLUDE_DIR
|
||||
NAMES
|
||||
gssapi.h
|
||||
gssapi/gssapi.h
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/include
|
||||
${_GSSAPI_INCLUDEDIR}
|
||||
)
|
||||
|
||||
if (GSSAPI_FLAVOR_MIT)
|
||||
find_library(GSSAPI_LIBRARY
|
||||
NAMES
|
||||
gssapi_krb5
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(KRB5_LIBRARY
|
||||
NAMES
|
||||
krb5
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(K5CRYPTO_LIBRARY
|
||||
NAMES
|
||||
k5crypto
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(COM_ERR_LIBRARY
|
||||
NAMES
|
||||
com_err
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
if (GSSAPI_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${GSSAPI_LIBRARY}
|
||||
)
|
||||
endif (GSSAPI_LIBRARY)
|
||||
|
||||
if (KRB5_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${KRB5_LIBRARY}
|
||||
)
|
||||
endif (KRB5_LIBRARY)
|
||||
|
||||
if (K5CRYPTO_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${K5CRYPTO_LIBRARY}
|
||||
)
|
||||
endif (K5CRYPTO_LIBRARY)
|
||||
|
||||
if (COM_ERR_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${COM_ERR_LIBRARY}
|
||||
)
|
||||
endif (COM_ERR_LIBRARY)
|
||||
endif (GSSAPI_FLAVOR_MIT)
|
||||
|
||||
if (GSSAPI_FLAVOR_HEIMDAL)
|
||||
find_library(GSSAPI_LIBRARY
|
||||
NAMES
|
||||
gssapi
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(KRB5_LIBRARY
|
||||
NAMES
|
||||
krb5
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(HCRYPTO_LIBRARY
|
||||
NAMES
|
||||
hcrypto
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(COM_ERR_LIBRARY
|
||||
NAMES
|
||||
com_err
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(HEIMNTLM_LIBRARY
|
||||
NAMES
|
||||
heimntlm
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(HX509_LIBRARY
|
||||
NAMES
|
||||
hx509
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(ASN1_LIBRARY
|
||||
NAMES
|
||||
asn1
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(WIND_LIBRARY
|
||||
NAMES
|
||||
wind
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
find_library(ROKEN_LIBRARY
|
||||
NAMES
|
||||
roken
|
||||
PATHS
|
||||
${GSSAPI_ROOT_DIR}/lib
|
||||
${_GSSAPI_LIBDIR}
|
||||
)
|
||||
|
||||
if (GSSAPI_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${GSSAPI_LIBRARY}
|
||||
)
|
||||
endif (GSSAPI_LIBRARY)
|
||||
|
||||
if (KRB5_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${KRB5_LIBRARY}
|
||||
)
|
||||
endif (KRB5_LIBRARY)
|
||||
|
||||
if (HCRYPTO_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${HCRYPTO_LIBRARY}
|
||||
)
|
||||
endif (HCRYPTO_LIBRARY)
|
||||
|
||||
if (COM_ERR_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${COM_ERR_LIBRARY}
|
||||
)
|
||||
endif (COM_ERR_LIBRARY)
|
||||
|
||||
if (HEIMNTLM_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${HEIMNTLM_LIBRARY}
|
||||
)
|
||||
endif (HEIMNTLM_LIBRARY)
|
||||
|
||||
if (HX509_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${HX509_LIBRARY}
|
||||
)
|
||||
endif (HX509_LIBRARY)
|
||||
|
||||
if (ASN1_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${ASN1_LIBRARY}
|
||||
)
|
||||
endif (ASN1_LIBRARY)
|
||||
|
||||
if (WIND_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${WIND_LIBRARY}
|
||||
)
|
||||
endif (WIND_LIBRARY)
|
||||
|
||||
if (ROKEN_LIBRARY)
|
||||
set(GSSAPI_LIBRARIES
|
||||
${GSSAPI_LIBRARIES}
|
||||
${WIND_LIBRARY}
|
||||
)
|
||||
endif (ROKEN_LIBRARY)
|
||||
endif (GSSAPI_FLAVOR_HEIMDAL)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR)
|
||||
|
||||
if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
|
||||
set(GSSAPI_FOUND TRUE)
|
||||
endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
|
||||
|
||||
# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
|
66
src/buffer.c
66
src/buffer.c
@ -189,10 +189,10 @@ int git_buf_puts(git_buf *buf, const char *string)
|
||||
return git_buf_put(buf, string, strlen(string));
|
||||
}
|
||||
|
||||
static const char b64str[] =
|
||||
static const char base64_encode[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
|
||||
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
|
||||
{
|
||||
size_t extra = len % 3;
|
||||
uint8_t *write, a, b, c;
|
||||
@ -207,19 +207,19 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
|
||||
b = *read++;
|
||||
c = *read++;
|
||||
|
||||
*write++ = b64str[a >> 2];
|
||||
*write++ = b64str[(a & 0x03) << 4 | b >> 4];
|
||||
*write++ = b64str[(b & 0x0f) << 2 | c >> 6];
|
||||
*write++ = b64str[c & 0x3f];
|
||||
*write++ = base64_encode[a >> 2];
|
||||
*write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
|
||||
*write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
|
||||
*write++ = base64_encode[c & 0x3f];
|
||||
}
|
||||
|
||||
if (extra > 0) {
|
||||
a = *read++;
|
||||
b = (extra > 1) ? *read++ : 0;
|
||||
|
||||
*write++ = b64str[a >> 2];
|
||||
*write++ = b64str[(a & 0x03) << 4 | b >> 4];
|
||||
*write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
|
||||
*write++ = base64_encode[a >> 2];
|
||||
*write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
|
||||
*write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
|
||||
*write++ = '=';
|
||||
}
|
||||
|
||||
@ -229,10 +229,56 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The inverse of base64_encode, offset by '+' == 43. */
|
||||
static const int8_t base64_decode[] = {
|
||||
62,
|
||||
-1, -1, -1,
|
||||
63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
|
||||
-1, -1, -1, 0, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
-1, -1, -1, -1, -1, -1,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||
};
|
||||
|
||||
#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
|
||||
|
||||
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
int8_t a, b, c, d;
|
||||
size_t orig_size = buf->size;
|
||||
|
||||
assert(len % 4 == 0);
|
||||
ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
|
||||
(b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
|
||||
(c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
|
||||
(d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
|
||||
buf->size = orig_size;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
|
||||
giterr_set(GITERR_INVALID, "Invalid base64 input");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
|
||||
buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
|
||||
buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
|
||||
}
|
||||
|
||||
buf->ptr[buf->size] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char b85str[] =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
|
||||
|
||||
int git_buf_put_base85(git_buf *buf, const char *data, size_t len)
|
||||
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
|
||||
{
|
||||
ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
|
||||
|
||||
|
@ -156,10 +156,12 @@ void git_buf_rtrim(git_buf *buf);
|
||||
int git_buf_cmp(const git_buf *a, const git_buf *b);
|
||||
|
||||
/* Write data as base64 encoded in buffer */
|
||||
int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
|
||||
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
|
||||
/* Decode the given bas64 and write the result to the buffer */
|
||||
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
|
||||
|
||||
/* Write data as "base85" encoded in buffer */
|
||||
int git_buf_put_base85(git_buf *buf, const char *data, size_t len);
|
||||
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
|
||||
|
||||
/*
|
||||
* Insert, remove or replace a portion of the buffer.
|
||||
|
@ -352,7 +352,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
|
||||
else
|
||||
git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
|
||||
|
||||
git_buf_put_base85(pi->buf, scan, chunk_len);
|
||||
git_buf_encode_base85(pi->buf, scan, chunk_len);
|
||||
git_buf_putc(pi->buf, '\n');
|
||||
|
||||
if (git_buf_oom(pi->buf)) {
|
||||
|
71
src/transports/auth.c
Normal file
71
src/transports/auth.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "git2.h"
|
||||
#include "buffer.h"
|
||||
#include "auth.h"
|
||||
|
||||
static int basic_next_token(
|
||||
git_buf *out, git_http_auth_context *ctx, git_cred *c)
|
||||
{
|
||||
git_cred_userpass_plaintext *cred;
|
||||
git_buf raw = GIT_BUF_INIT;
|
||||
int error = -1;
|
||||
|
||||
GIT_UNUSED(ctx);
|
||||
|
||||
if (c->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
|
||||
giterr_set(GITERR_INVALID, "invalid credential type for basic auth");
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
cred = (git_cred_userpass_plaintext *)c;
|
||||
|
||||
git_buf_printf(&raw, "%s:%s", cred->username, cred->password);
|
||||
|
||||
if (git_buf_oom(&raw) ||
|
||||
git_buf_puts(out, "Authorization: Basic ") < 0 ||
|
||||
git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0 ||
|
||||
git_buf_puts(out, "\r\n") < 0)
|
||||
goto on_error;
|
||||
|
||||
error = 0;
|
||||
|
||||
on_error:
|
||||
if (raw.size)
|
||||
git__memzero(raw.ptr, raw.size);
|
||||
|
||||
git_buf_free(&raw);
|
||||
return error;
|
||||
}
|
||||
|
||||
static git_http_auth_context basic_context = {
|
||||
GIT_AUTHTYPE_BASIC,
|
||||
GIT_CREDTYPE_USERPASS_PLAINTEXT,
|
||||
NULL,
|
||||
basic_next_token,
|
||||
NULL
|
||||
};
|
||||
|
||||
int git_http_auth_basic(
|
||||
git_http_auth_context **out, const gitno_connection_data *connection_data)
|
||||
{
|
||||
GIT_UNUSED(connection_data);
|
||||
|
||||
*out = &basic_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_http_auth_dummy(
|
||||
git_http_auth_context **out, const gitno_connection_data *connection_data)
|
||||
{
|
||||
GIT_UNUSED(connection_data);
|
||||
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
63
src/transports/auth.h
Normal file
63
src/transports/auth.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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_http_auth_h__
|
||||
#define INCLUDE_http_auth_h__
|
||||
|
||||
#include "git2.h"
|
||||
#include "netops.h"
|
||||
|
||||
typedef enum {
|
||||
GIT_AUTHTYPE_BASIC = 1,
|
||||
GIT_AUTHTYPE_NEGOTIATE = 2,
|
||||
} git_http_authtype_t;
|
||||
|
||||
typedef struct git_http_auth_context git_http_auth_context;
|
||||
|
||||
struct git_http_auth_context {
|
||||
/** Type of scheme */
|
||||
git_http_authtype_t type;
|
||||
|
||||
/** Supported credentials */
|
||||
git_credtype_t credtypes;
|
||||
|
||||
/** Sets the challenge on the authentication context */
|
||||
int (*set_challenge)(git_http_auth_context *ctx, const char *challenge);
|
||||
|
||||
/** Gets the next authentication token from the context */
|
||||
int (*next_token)(git_buf *out, git_http_auth_context *ctx, git_cred *cred);
|
||||
|
||||
/** Frees the authentication context */
|
||||
void (*free)(git_http_auth_context *ctx);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/** Type of scheme */
|
||||
git_http_authtype_t type;
|
||||
|
||||
/** Name of the scheme (as used in the Authorization header) */
|
||||
const char *name;
|
||||
|
||||
/** Credential types this scheme supports */
|
||||
git_credtype_t credtypes;
|
||||
|
||||
/** Function to initialize an authentication context */
|
||||
int (*init_context)(
|
||||
git_http_auth_context **out,
|
||||
const gitno_connection_data *connection_data);
|
||||
} git_http_auth_scheme;
|
||||
|
||||
int git_http_auth_dummy(
|
||||
git_http_auth_context **out,
|
||||
const gitno_connection_data *connection_data);
|
||||
|
||||
int git_http_auth_basic(
|
||||
git_http_auth_context **out,
|
||||
const gitno_connection_data *connection_data);
|
||||
|
||||
#endif
|
||||
|
275
src/transports/auth_negotiate.c
Normal file
275
src/transports/auth_negotiate.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef GIT_GSSAPI
|
||||
|
||||
#include "git2.h"
|
||||
#include "common.h"
|
||||
#include "buffer.h"
|
||||
#include "auth.h"
|
||||
|
||||
#include <gssapi.h>
|
||||
#include <krb5.h>
|
||||
|
||||
static gss_OID_desc negotiate_oid_spnego =
|
||||
{ 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
|
||||
static gss_OID_desc negotiate_oid_krb5 =
|
||||
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
|
||||
|
||||
static gss_OID negotiate_oids[] =
|
||||
{ &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
|
||||
|
||||
typedef struct {
|
||||
git_http_auth_context parent;
|
||||
unsigned configured : 1,
|
||||
complete : 1;
|
||||
git_buf target;
|
||||
char *challenge;
|
||||
gss_ctx_id_t gss_context;
|
||||
gss_OID oid;
|
||||
} http_auth_negotiate_context;
|
||||
|
||||
static void negotiate_err_set(
|
||||
OM_uint32 status_major,
|
||||
OM_uint32 status_minor,
|
||||
const char *message)
|
||||
{
|
||||
gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 status_display, context = 0;
|
||||
|
||||
if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
|
||||
GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
|
||||
giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
|
||||
message, (int)buffer.length, (const char *)buffer.value,
|
||||
status_major, status_minor);
|
||||
gss_release_buffer(&status_minor, &buffer);
|
||||
} else {
|
||||
giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
|
||||
message, status_major, status_minor);
|
||||
}
|
||||
}
|
||||
|
||||
static int negotiate_set_challenge(
|
||||
git_http_auth_context *c,
|
||||
const char *challenge)
|
||||
{
|
||||
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
|
||||
|
||||
assert(ctx && ctx->configured && challenge);
|
||||
|
||||
git__free(ctx->challenge);
|
||||
|
||||
ctx->challenge = git__strdup(challenge);
|
||||
GITERR_CHECK_ALLOC(ctx->challenge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int negotiate_next_token(
|
||||
git_buf *buf,
|
||||
git_http_auth_context *c,
|
||||
git_cred *cred)
|
||||
{
|
||||
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
|
||||
OM_uint32 status_major, status_minor;
|
||||
gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
|
||||
input_token = GSS_C_EMPTY_BUFFER,
|
||||
output_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
|
||||
git_buf input_buf = GIT_BUF_INIT;
|
||||
gss_name_t server = NULL;
|
||||
gss_OID mech;
|
||||
size_t challenge_len;
|
||||
int error = 0;
|
||||
|
||||
assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
|
||||
|
||||
if (ctx->complete)
|
||||
return 0;
|
||||
|
||||
target_buffer.value = (void *)ctx->target.ptr;
|
||||
target_buffer.length = ctx->target.size;
|
||||
|
||||
status_major = gss_import_name(&status_minor, &target_buffer,
|
||||
GSS_C_NT_HOSTBASED_SERVICE, &server);
|
||||
|
||||
if (GSS_ERROR(status_major)) {
|
||||
negotiate_err_set(status_major, status_minor,
|
||||
"Could not parse principal");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
|
||||
|
||||
if (challenge_len < 9) {
|
||||
giterr_set(GITERR_NET, "No negotiate challenge sent from server");
|
||||
error = -1;
|
||||
goto done;
|
||||
} else if (challenge_len > 9) {
|
||||
if (git_buf_decode_base64(&input_buf,
|
||||
ctx->challenge + 10, challenge_len - 10) < 0) {
|
||||
giterr_set(GITERR_NET, "Invalid negotiate challenge from server");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
input_token.value = input_buf.ptr;
|
||||
input_token.length = input_buf.size;
|
||||
input_token_ptr = &input_token;
|
||||
} else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
|
||||
giterr_set(GITERR_NET, "Could not restart authentication");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mech = &negotiate_oid_spnego;
|
||||
|
||||
if (GSS_ERROR(status_major = gss_init_sec_context(
|
||||
&status_minor,
|
||||
GSS_C_NO_CREDENTIAL,
|
||||
&ctx->gss_context,
|
||||
server,
|
||||
mech,
|
||||
GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
|
||||
GSS_C_INDEFINITE,
|
||||
GSS_C_NO_CHANNEL_BINDINGS,
|
||||
input_token_ptr,
|
||||
NULL,
|
||||
&output_token,
|
||||
NULL,
|
||||
NULL))) {
|
||||
negotiate_err_set(status_major, status_minor, "Negotiate failure");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* This message merely told us auth was complete; we do not respond. */
|
||||
if (status_major == GSS_S_COMPLETE) {
|
||||
ctx->complete = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
git_buf_puts(buf, "Authorization: Negotiate ");
|
||||
git_buf_encode_base64(buf, output_token.value, output_token.length);
|
||||
git_buf_puts(buf, "\r\n");
|
||||
|
||||
if (git_buf_oom(buf))
|
||||
error = -1;
|
||||
|
||||
done:
|
||||
gss_release_name(&status_minor, &server);
|
||||
gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
|
||||
git_buf_free(&input_buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void negotiate_context_free(git_http_auth_context *c)
|
||||
{
|
||||
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
|
||||
OM_uint32 status_minor;
|
||||
|
||||
if (ctx->gss_context != GSS_C_NO_CONTEXT) {
|
||||
gss_delete_sec_context(
|
||||
&status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
|
||||
ctx->gss_context = GSS_C_NO_CONTEXT;
|
||||
}
|
||||
|
||||
git_buf_free(&ctx->target);
|
||||
|
||||
git__free(ctx->challenge);
|
||||
|
||||
ctx->configured = 0;
|
||||
ctx->complete = 0;
|
||||
ctx->oid = NULL;
|
||||
|
||||
git__free(ctx);
|
||||
}
|
||||
|
||||
static int negotiate_init_context(
|
||||
http_auth_negotiate_context *ctx,
|
||||
const gitno_connection_data *connection_data)
|
||||
{
|
||||
OM_uint32 status_major, status_minor;
|
||||
gss_OID item, *oid;
|
||||
gss_OID_set mechanism_list;
|
||||
size_t i;
|
||||
|
||||
/* Query supported mechanisms looking for SPNEGO) */
|
||||
if (GSS_ERROR(status_major =
|
||||
gss_indicate_mechs(&status_minor, &mechanism_list))) {
|
||||
negotiate_err_set(status_major, status_minor,
|
||||
"could not query mechanisms");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mechanism_list) {
|
||||
for (oid = negotiate_oids; *oid; oid++) {
|
||||
for (i = 0; i < mechanism_list->count; i++) {
|
||||
item = &mechanism_list->elements[i];
|
||||
|
||||
if (item->length == (*oid)->length &&
|
||||
memcmp(item->elements, (*oid)->elements, item->length) == 0) {
|
||||
ctx->oid = *oid;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ctx->oid)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gss_release_oid_set(&status_minor, &mechanism_list);
|
||||
|
||||
if (!ctx->oid) {
|
||||
giterr_set(GITERR_NET, "Negotiate authentication is not supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_buf_puts(&ctx->target, "HTTP@");
|
||||
git_buf_puts(&ctx->target, connection_data->host);
|
||||
|
||||
if (git_buf_oom(&ctx->target))
|
||||
return -1;
|
||||
|
||||
ctx->gss_context = GSS_C_NO_CONTEXT;
|
||||
ctx->configured = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_http_auth_negotiate(
|
||||
git_http_auth_context **out,
|
||||
const gitno_connection_data *connection_data)
|
||||
{
|
||||
http_auth_negotiate_context *ctx;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
|
||||
GITERR_CHECK_ALLOC(ctx);
|
||||
|
||||
if (negotiate_init_context(ctx, connection_data) < 0) {
|
||||
git__free(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
|
||||
ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
|
||||
ctx->parent.set_challenge = negotiate_set_challenge;
|
||||
ctx->parent.next_token = negotiate_next_token;
|
||||
ctx->parent.free = negotiate_context_free;
|
||||
|
||||
*out = (git_http_auth_context *)ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* GIT_GSSAPI */
|
||||
|
27
src/transports/auth_negotiate.h
Normal file
27
src/transports/auth_negotiate.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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_auth_negotiate_h__
|
||||
#define INCLUDE_auth_negotiate_h__
|
||||
|
||||
#include "git2.h"
|
||||
#include "auth.h"
|
||||
|
||||
#ifdef GIT_GSSAPI
|
||||
|
||||
extern int git_http_auth_negotiate(
|
||||
git_http_auth_context **out,
|
||||
const gitno_connection_data *connection_data);
|
||||
|
||||
#else
|
||||
|
||||
#define git_http_auth_negotiate git_http_auth_dummy
|
||||
|
||||
#endif /* GIT_GSSAPI */
|
||||
|
||||
#endif
|
||||
|
@ -11,6 +11,13 @@
|
||||
#include "buffer.h"
|
||||
#include "netops.h"
|
||||
#include "smart.h"
|
||||
#include "auth.h"
|
||||
#include "auth_negotiate.h"
|
||||
|
||||
git_http_auth_scheme auth_schemes[] = {
|
||||
{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
|
||||
{ GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
|
||||
};
|
||||
|
||||
static const char *upload_pack_service = "upload-pack";
|
||||
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
|
||||
@ -20,7 +27,6 @@ static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive
|
||||
static const char *receive_pack_service_url = "/git-receive-pack";
|
||||
static const char *get_verb = "GET";
|
||||
static const char *post_verb = "POST";
|
||||
static const char *basic_authtype = "Basic";
|
||||
|
||||
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
|
||||
|
||||
@ -35,10 +41,6 @@ enum last_cb {
|
||||
VALUE
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GIT_HTTP_AUTH_BASIC = 1,
|
||||
} http_authmechanism_t;
|
||||
|
||||
typedef struct {
|
||||
git_smart_subtransport_stream parent;
|
||||
const char *service;
|
||||
@ -58,9 +60,6 @@ typedef struct {
|
||||
transport_smart *owner;
|
||||
gitno_socket socket;
|
||||
gitno_connection_data connection_data;
|
||||
git_cred *cred;
|
||||
git_cred *url_cred;
|
||||
http_authmechanism_t auth_mechanism;
|
||||
bool connected;
|
||||
|
||||
/* Parser structures */
|
||||
@ -76,6 +75,11 @@ typedef struct {
|
||||
enum last_cb last_cb;
|
||||
int parse_error;
|
||||
unsigned parse_finished : 1;
|
||||
|
||||
/* Authentication */
|
||||
git_cred *cred;
|
||||
git_cred *url_cred;
|
||||
git_vector auth_contexts;
|
||||
} http_subtransport;
|
||||
|
||||
typedef struct {
|
||||
@ -88,28 +92,91 @@ typedef struct {
|
||||
size_t *bytes_read;
|
||||
} parser_context;
|
||||
|
||||
static int apply_basic_credential(git_buf *buf, git_cred *cred)
|
||||
static bool credtype_match(git_http_auth_scheme *scheme, void *data)
|
||||
{
|
||||
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
|
||||
git_buf raw = GIT_BUF_INIT;
|
||||
int error = -1;
|
||||
unsigned int credtype = *(unsigned int *)data;
|
||||
|
||||
git_buf_printf(&raw, "%s:%s", c->username, c->password);
|
||||
return !!(scheme->credtypes & credtype);
|
||||
}
|
||||
|
||||
if (git_buf_oom(&raw) ||
|
||||
git_buf_puts(buf, "Authorization: Basic ") < 0 ||
|
||||
git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 ||
|
||||
git_buf_puts(buf, "\r\n") < 0)
|
||||
goto on_error;
|
||||
static bool challenge_match(git_http_auth_scheme *scheme, void *data)
|
||||
{
|
||||
const char *scheme_name = scheme->name;
|
||||
const char *challenge = (const char *)data;
|
||||
size_t scheme_len;
|
||||
|
||||
error = 0;
|
||||
scheme_len = strlen(scheme_name);
|
||||
return (strncmp(challenge, scheme_name, scheme_len) == 0 &&
|
||||
(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
|
||||
}
|
||||
|
||||
on_error:
|
||||
if (raw.size)
|
||||
memset(raw.ptr, 0x0, raw.size);
|
||||
static int auth_context_match(
|
||||
git_http_auth_context **out,
|
||||
http_subtransport *t,
|
||||
bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
|
||||
void *data)
|
||||
{
|
||||
git_http_auth_scheme *scheme = NULL;
|
||||
git_http_auth_context *context = NULL, *c;
|
||||
size_t i;
|
||||
|
||||
git_buf_free(&raw);
|
||||
return error;
|
||||
*out = NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
|
||||
if (scheme_match(&auth_schemes[i], data)) {
|
||||
scheme = &auth_schemes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scheme)
|
||||
return 0;
|
||||
|
||||
/* See if authentication has already started for this scheme */
|
||||
git_vector_foreach(&t->auth_contexts, i, c) {
|
||||
if (c->type == scheme->type) {
|
||||
context = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!context) {
|
||||
if (scheme->init_context(&context, &t->connection_data) < 0)
|
||||
return -1;
|
||||
else if (!context)
|
||||
return 0;
|
||||
else if (git_vector_insert(&t->auth_contexts, context) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_credentials(git_buf *buf, http_subtransport *t)
|
||||
{
|
||||
git_cred *cred = t->cred;
|
||||
git_http_auth_context *context;
|
||||
|
||||
/* Apply the credentials given to us in the URL */
|
||||
if (!cred && t->connection_data.user && t->connection_data.pass) {
|
||||
if (!t->url_cred &&
|
||||
git_cred_userpass_plaintext_new(&t->url_cred,
|
||||
t->connection_data.user, t->connection_data.pass) < 0)
|
||||
return -1;
|
||||
|
||||
cred = t->url_cred;
|
||||
}
|
||||
|
||||
if (!cred)
|
||||
return 0;
|
||||
|
||||
/* Get or create a context for the best scheme for this cred type */
|
||||
if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
|
||||
return -1;
|
||||
|
||||
return context->next_token(buf, context, cred);
|
||||
}
|
||||
|
||||
static int gen_request(
|
||||
@ -137,19 +204,9 @@ static int gen_request(
|
||||
git_buf_puts(buf, "Accept: */*\r\n");
|
||||
|
||||
/* Apply credentials to the request */
|
||||
if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
|
||||
t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
|
||||
apply_basic_credential(buf, t->cred) < 0)
|
||||
if (apply_credentials(buf, t) < 0)
|
||||
return -1;
|
||||
|
||||
/* Use url-parsed basic auth if username and password are both provided */
|
||||
if (!t->cred && t->connection_data.user && t->connection_data.pass) {
|
||||
if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
|
||||
t->connection_data.user, t->connection_data.pass) < 0)
|
||||
return -1;
|
||||
if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
|
||||
}
|
||||
|
||||
git_buf_puts(buf, "\r\n");
|
||||
|
||||
if (git_buf_oom(buf))
|
||||
@ -158,20 +215,26 @@ static int gen_request(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_unauthorized_response(
|
||||
static int parse_authenticate_response(
|
||||
git_vector *www_authenticate,
|
||||
int *allowed_types,
|
||||
http_authmechanism_t *auth_mechanism)
|
||||
http_subtransport *t,
|
||||
int *allowed_types)
|
||||
{
|
||||
unsigned i;
|
||||
char *entry;
|
||||
git_http_auth_context *context;
|
||||
char *challenge;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(www_authenticate, i, entry) {
|
||||
if (!strncmp(entry, basic_authtype, 5) &&
|
||||
(entry[5] == '\0' || entry[5] == ' ')) {
|
||||
*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
|
||||
*auth_mechanism = GIT_HTTP_AUTH_BASIC;
|
||||
}
|
||||
git_vector_foreach(www_authenticate, i, challenge) {
|
||||
if (auth_context_match(&context, t, challenge_match, challenge) < 0)
|
||||
return -1;
|
||||
else if (!context)
|
||||
continue;
|
||||
|
||||
if (context->set_challenge &&
|
||||
context->set_challenge(context, challenge) < 0)
|
||||
return -1;
|
||||
|
||||
*allowed_types |= context->credtypes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -248,7 +311,7 @@ static int on_headers_complete(http_parser *parser)
|
||||
http_subtransport *t = ctx->t;
|
||||
http_stream *s = ctx->s;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
int error = 0, no_callback = 0;
|
||||
int error = 0, no_callback = 0, allowed_auth_types = 0;
|
||||
|
||||
/* Both parse_header_name and parse_header_value are populated
|
||||
* and ready for consumption. */
|
||||
@ -256,26 +319,26 @@ static int on_headers_complete(http_parser *parser)
|
||||
if (on_header_ready(t) < 0)
|
||||
return t->parse_error = PARSE_ERROR_GENERIC;
|
||||
|
||||
/* Check for an authentication failure. */
|
||||
/* Capture authentication headers which may be a 401 (authentication
|
||||
* is not complete) or a 200 (simply informing us that auth *is*
|
||||
* complete.)
|
||||
*/
|
||||
if (parse_authenticate_response(&t->www_authenticate, t,
|
||||
&allowed_auth_types) < 0)
|
||||
return t->parse_error = PARSE_ERROR_GENERIC;
|
||||
|
||||
if (parser->status_code == 401 &&
|
||||
get_verb == s->verb) {
|
||||
/* Check for an authentication failure. */
|
||||
if (parser->status_code == 401 && get_verb == s->verb) {
|
||||
if (!t->owner->cred_acquire_cb) {
|
||||
no_callback = 1;
|
||||
} else {
|
||||
int allowed_types = 0;
|
||||
|
||||
if (parse_unauthorized_response(&t->www_authenticate,
|
||||
&allowed_types, &t->auth_mechanism) < 0)
|
||||
return t->parse_error = PARSE_ERROR_GENERIC;
|
||||
|
||||
if (allowed_types &&
|
||||
(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
|
||||
if (allowed_auth_types &&
|
||||
(!t->cred || 0 == (t->cred->credtype & allowed_auth_types))) {
|
||||
|
||||
error = t->owner->cred_acquire_cb(&t->cred,
|
||||
t->owner->url,
|
||||
t->connection_data.user,
|
||||
allowed_types,
|
||||
allowed_auth_types,
|
||||
t->owner->cred_acquire_payload);
|
||||
|
||||
if (error == GIT_PASSTHROUGH) {
|
||||
@ -286,7 +349,8 @@ static int on_headers_complete(http_parser *parser)
|
||||
assert(t->cred);
|
||||
|
||||
/* Successfully acquired a credential. */
|
||||
return t->parse_error = PARSE_ERROR_REPLAY;
|
||||
t->parse_error = PARSE_ERROR_REPLAY;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -324,7 +388,8 @@ static int on_headers_complete(http_parser *parser)
|
||||
t->connected = 0;
|
||||
s->redirect_count++;
|
||||
|
||||
return t->parse_error = PARSE_ERROR_REPLAY;
|
||||
t->parse_error = PARSE_ERROR_REPLAY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for a 200 HTTP status code. */
|
||||
@ -382,6 +447,13 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
|
||||
parser_context *ctx = (parser_context *) parser->data;
|
||||
http_subtransport *t = ctx->t;
|
||||
|
||||
/* If our goal is to replay the request (either an auth failure or
|
||||
* a redirect) then don't bother buffering since we're ignoring the
|
||||
* content anyway.
|
||||
*/
|
||||
if (t->parse_error == PARSE_ERROR_REPLAY)
|
||||
return 0;
|
||||
|
||||
if (ctx->buf_size < len) {
|
||||
giterr_set(GITERR_NET, "Can't fit data in the buffer");
|
||||
return t->parse_error = PARSE_ERROR_GENERIC;
|
||||
@ -456,7 +528,7 @@ static int http_connect(http_subtransport *t)
|
||||
|
||||
if (t->connected &&
|
||||
http_should_keep_alive(&t->parser) &&
|
||||
http_body_is_final(&t->parser))
|
||||
t->parse_finished)
|
||||
return 0;
|
||||
|
||||
if (t->socket.socket)
|
||||
@ -502,10 +574,8 @@ replay:
|
||||
|
||||
clear_parser_state(t);
|
||||
|
||||
if (gen_request(&request, s, 0) < 0) {
|
||||
giterr_set(GITERR_NET, "Failed to generate request");
|
||||
if (gen_request(&request, s, 0) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
|
||||
git_buf_free(&request);
|
||||
@ -604,10 +674,8 @@ static int http_stream_write_chunked(
|
||||
|
||||
clear_parser_state(t);
|
||||
|
||||
if (gen_request(&request, s, 0) < 0) {
|
||||
giterr_set(GITERR_NET, "Failed to generate request");
|
||||
if (gen_request(&request, s, 0) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
|
||||
git_buf_free(&request);
|
||||
@ -679,10 +747,8 @@ static int http_stream_write_single(
|
||||
|
||||
clear_parser_state(t);
|
||||
|
||||
if (gen_request(&request, s, len) < 0) {
|
||||
giterr_set(GITERR_NET, "Failed to generate request");
|
||||
if (gen_request(&request, s, len) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
|
||||
goto on_error;
|
||||
@ -849,6 +915,8 @@ static int http_action(
|
||||
static int http_close(git_smart_subtransport *subtransport)
|
||||
{
|
||||
http_subtransport *t = (http_subtransport *) subtransport;
|
||||
git_http_auth_context *context;
|
||||
size_t i;
|
||||
|
||||
clear_parser_state(t);
|
||||
|
||||
@ -867,6 +935,13 @@ static int http_close(git_smart_subtransport *subtransport)
|
||||
t->url_cred = NULL;
|
||||
}
|
||||
|
||||
git_vector_foreach(&t->auth_contexts, i, context) {
|
||||
if (context->free)
|
||||
context->free(context);
|
||||
}
|
||||
|
||||
git_vector_clear(&t->auth_contexts);
|
||||
|
||||
gitno_connection_data_free_ptrs(&t->connection_data);
|
||||
|
||||
return 0;
|
||||
@ -878,6 +953,7 @@ static void http_free(git_smart_subtransport *subtransport)
|
||||
|
||||
http_close(subtransport);
|
||||
|
||||
git_vector_free(&t->auth_contexts);
|
||||
git__free(t);
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ static int apply_basic_credential(HINTERNET request, git_cred *cred)
|
||||
|
||||
if (git_buf_oom(&raw) ||
|
||||
git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
|
||||
git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
|
||||
git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
|
||||
goto on_error;
|
||||
|
||||
if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
|
||||
|
@ -748,7 +748,7 @@ void test_core_buffer__unescape(void)
|
||||
assert_unescape("", "");
|
||||
}
|
||||
|
||||
void test_core_buffer__base64(void)
|
||||
void test_core_buffer__encode_base64(void)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
@ -759,33 +759,54 @@ void test_core_buffer__base64(void)
|
||||
* 0x 1d 06 21 29 1c 30
|
||||
* d G h p c w
|
||||
*/
|
||||
cl_git_pass(git_buf_put_base64(&buf, "this", 4));
|
||||
cl_git_pass(git_buf_encode_base64(&buf, "this", 4));
|
||||
cl_assert_equal_s("dGhpcw==", buf.ptr);
|
||||
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_put_base64(&buf, "this!", 5));
|
||||
cl_git_pass(git_buf_encode_base64(&buf, "this!", 5));
|
||||
cl_assert_equal_s("dGhpcyE=", buf.ptr);
|
||||
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6));
|
||||
cl_git_pass(git_buf_encode_base64(&buf, "this!\n", 6));
|
||||
cl_assert_equal_s("dGhpcyEK", buf.ptr);
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_core_buffer__base85(void)
|
||||
void test_core_buffer__decode_base64(void)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_put_base85(&buf, "this", 4));
|
||||
cl_git_pass(git_buf_decode_base64(&buf, "dGhpcw==", 8));
|
||||
cl_assert_equal_s("this", buf.ptr);
|
||||
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyE=", 8));
|
||||
cl_assert_equal_s("this!", buf.ptr);
|
||||
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyEK", 8));
|
||||
cl_assert_equal_s("this!\n", buf.ptr);
|
||||
|
||||
cl_git_fail(git_buf_decode_base64(&buf, "This is not a valid base64 string!!!", 36));
|
||||
cl_assert_equal_s("this!\n", buf.ptr);
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_core_buffer__encode_base85(void)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_encode_base85(&buf, "this", 4));
|
||||
cl_assert_equal_s("bZBXF", buf.ptr);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
cl_git_pass(git_buf_put_base85(&buf, "two rnds", 8));
|
||||
cl_git_pass(git_buf_encode_base85(&buf, "two rnds", 8));
|
||||
cl_assert_equal_s("ba!tca&BaE", buf.ptr);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
cl_git_pass(git_buf_put_base85(&buf, "this is base 85 encoded",
|
||||
cl_git_pass(git_buf_encode_base85(&buf, "this is base 85 encoded",
|
||||
strlen("this is base 85 encoded")));
|
||||
cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr);
|
||||
git_buf_clear(&buf);
|
||||
|
@ -226,9 +226,28 @@ void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
|
||||
cl_git_fail_with(git_clone(&g_repo, remote_url, "./foo", &g_options), -1);
|
||||
}
|
||||
|
||||
int cred_default(
|
||||
git_cred **cred,
|
||||
const char *url,
|
||||
const char *user_from_url,
|
||||
unsigned int allowed_types,
|
||||
void *payload)
|
||||
{
|
||||
GIT_UNUSED(url);
|
||||
GIT_UNUSED(user_from_url);
|
||||
GIT_UNUSED(payload);
|
||||
|
||||
if (!(allowed_types & GIT_CREDTYPE_DEFAULT))
|
||||
return 0;
|
||||
|
||||
return git_cred_default_new(cred);
|
||||
}
|
||||
|
||||
void test_online_clone__credentials(void)
|
||||
{
|
||||
/* Remote URL environment variable must be set. User and password are optional. */
|
||||
/* Remote URL environment variable must be set.
|
||||
* User and password are optional.
|
||||
*/
|
||||
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
|
||||
git_cred_userpass_payload user_pass = {
|
||||
cl_getenv("GITTEST_REMOTE_USER"),
|
||||
@ -237,8 +256,12 @@ void test_online_clone__credentials(void)
|
||||
|
||||
if (!remote_url) return;
|
||||
|
||||
g_options.remote_callbacks.credentials = git_cred_userpass;
|
||||
g_options.remote_callbacks.payload = &user_pass;
|
||||
if (cl_getenv("GITTEST_REMOTE_DEFAULT")) {
|
||||
g_options.remote_callbacks.credentials = cred_default;
|
||||
} else {
|
||||
g_options.remote_callbacks.credentials = git_cred_userpass;
|
||||
g_options.remote_callbacks.payload = &user_pass;
|
||||
}
|
||||
|
||||
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
|
||||
git_repository_free(g_repo); g_repo = NULL;
|
||||
@ -346,7 +369,7 @@ void test_online_clone__ssh_with_paths(void)
|
||||
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
|
||||
const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
|
||||
|
||||
if (!remote_url || !remote_user)
|
||||
if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0)
|
||||
clar__skip();
|
||||
|
||||
g_options.remote_cb = custom_remote_ssh_with_paths;
|
||||
|
@ -204,6 +204,8 @@ static void verify_tracking_branches(git_remote *remote, expected_ref expected_r
|
||||
cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
|
||||
|
||||
cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
|
||||
|
||||
git_reference_free(ref);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
@ -852,6 +854,9 @@ void test_online_push__notes(void)
|
||||
const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
|
||||
push_status exp_stats[] = { { "refs/notes/commits", 1 } };
|
||||
expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
|
||||
const char *specs_del[] = { ":refs/notes/commits" };
|
||||
expected_ref exp_refs_del[] = { };
|
||||
|
||||
git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
|
||||
|
||||
target_oid = &_oid_b6;
|
||||
@ -864,5 +869,11 @@ void test_online_push__notes(void)
|
||||
exp_stats, ARRAY_SIZE(exp_stats),
|
||||
exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
|
||||
|
||||
/* And make sure to delete the note */
|
||||
|
||||
do_push(specs_del, ARRAY_SIZE(specs_del),
|
||||
exp_stats, 1,
|
||||
exp_refs_del, ARRAY_SIZE(exp_refs_del), 0, 0, 0);
|
||||
|
||||
git_signature_free(signature);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user