mirror of
https://git.proxmox.com/git/libgit2
synced 2026-02-01 19:03:40 +00:00
Add GSSAPI support for SPNEGO/Kerberos auth over HTTP
This commit is contained in:
parent
e003f83a58
commit
315cb38e1e
@ -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" ON )
|
||||
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)
|
||||
@ -12,6 +12,11 @@
|
||||
#include "netops.h"
|
||||
#include "smart.h"
|
||||
|
||||
#ifdef GIT_GSSAPI
|
||||
# include <gssapi.h>
|
||||
# include <krb5.h>
|
||||
#endif
|
||||
|
||||
static const char *upload_pack_service = "upload-pack";
|
||||
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
|
||||
static const char *upload_pack_service_url = "/git-upload-pack";
|
||||
@ -21,6 +26,17 @@ 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";
|
||||
static const char *negotiate_authtype = "Negotiate";
|
||||
|
||||
#ifdef GIT_GSSAPI
|
||||
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 };
|
||||
#endif
|
||||
|
||||
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
|
||||
|
||||
@ -37,6 +53,7 @@ enum last_cb {
|
||||
|
||||
typedef enum {
|
||||
GIT_HTTP_AUTH_BASIC = 1,
|
||||
GIT_HTTP_AUTH_NEGOTIATE = 2,
|
||||
} http_authmechanism_t;
|
||||
|
||||
typedef struct {
|
||||
@ -61,6 +78,7 @@ typedef struct {
|
||||
git_cred *cred;
|
||||
git_cred *url_cred;
|
||||
http_authmechanism_t auth_mechanism;
|
||||
char *auth_challenge;
|
||||
bool connected;
|
||||
|
||||
/* Parser structures */
|
||||
@ -76,6 +94,14 @@ typedef struct {
|
||||
enum last_cb last_cb;
|
||||
int parse_error;
|
||||
unsigned parse_finished : 1;
|
||||
|
||||
#ifdef GIT_GSSAPI
|
||||
unsigned negotiate_configured : 1,
|
||||
negotiate_complete : 1;
|
||||
git_buf negotiate_target;
|
||||
gss_ctx_id_t negotiate_context;
|
||||
gss_OID negotiate_oid;
|
||||
#endif
|
||||
} http_subtransport;
|
||||
|
||||
typedef struct {
|
||||
@ -88,12 +114,17 @@ typedef struct {
|
||||
size_t *bytes_read;
|
||||
} parser_context;
|
||||
|
||||
static int apply_basic_credential(git_buf *buf, git_cred *cred)
|
||||
static int apply_basic_credential(
|
||||
git_buf *buf,
|
||||
http_subtransport *transport,
|
||||
git_cred *cred)
|
||||
{
|
||||
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
|
||||
git_buf raw = GIT_BUF_INIT;
|
||||
int error = -1;
|
||||
|
||||
GIT_UNUSED(transport);
|
||||
|
||||
git_buf_printf(&raw, "%s:%s", c->username, c->password);
|
||||
|
||||
if (git_buf_oom(&raw) ||
|
||||
@ -112,6 +143,188 @@ on_error:
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef GIT_GSSAPI
|
||||
|
||||
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_configure(http_subtransport *transport)
|
||||
{
|
||||
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) {
|
||||
transport->negotiate_oid = *oid;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (transport->negotiate_oid)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gss_release_oid_set(&status_minor, &mechanism_list);
|
||||
|
||||
if (!transport->negotiate_oid) {
|
||||
giterr_set(GITERR_NET, "Negotiate authentication is not supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_buf_puts(&transport->negotiate_target, "HTTP@");
|
||||
git_buf_puts(&transport->negotiate_target, transport->connection_data.host);
|
||||
|
||||
if (git_buf_oom(&transport->negotiate_target))
|
||||
return -1;
|
||||
|
||||
transport->negotiate_context = GSS_C_NO_CONTEXT;
|
||||
transport->negotiate_configured = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int negotiate_next_token(
|
||||
git_buf *buf,
|
||||
http_subtransport *transport,
|
||||
git_cred *cred)
|
||||
{
|
||||
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;
|
||||
|
||||
GIT_UNUSED(cred);
|
||||
|
||||
target_buffer.value = (void *)transport->negotiate_target.ptr;
|
||||
target_buffer.length = transport->negotiate_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 = transport->auth_challenge ?
|
||||
strlen(transport->auth_challenge) : 0;
|
||||
assert(challenge_len >= 9);
|
||||
|
||||
if (challenge_len > 9) {
|
||||
if (git_buf_decode_base64(&input_buf,
|
||||
transport->auth_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 (transport->negotiate_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,
|
||||
&transport->negotiate_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) {
|
||||
transport->negotiate_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 int apply_negotiate_credential(
|
||||
git_buf *buf,
|
||||
http_subtransport *transport,
|
||||
git_cred *cred)
|
||||
{
|
||||
if (!transport->negotiate_configured && negotiate_configure(transport) < 0)
|
||||
return -1;
|
||||
|
||||
if (transport->negotiate_complete)
|
||||
return 0;
|
||||
|
||||
return negotiate_next_token(buf, transport, cred);
|
||||
}
|
||||
|
||||
#endif /* GIT_GSSAPI */
|
||||
|
||||
static int gen_request(
|
||||
git_buf *buf,
|
||||
http_stream *s,
|
||||
@ -137,17 +350,26 @@ static int gen_request(
|
||||
git_buf_puts(buf, "Accept: */*\r\n");
|
||||
|
||||
/* Apply credentials to the request */
|
||||
#ifdef GIT_GSSAPI
|
||||
if (t->cred && t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
|
||||
(t->auth_mechanism & GIT_HTTP_AUTH_NEGOTIATE)) {
|
||||
if (apply_negotiate_credential(buf, t, t->cred) < 0)
|
||||
return -1;
|
||||
} else
|
||||
#endif
|
||||
|
||||
if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
|
||||
t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
|
||||
apply_basic_credential(buf, t->cred) < 0)
|
||||
return -1;
|
||||
t->auth_mechanism == GIT_HTTP_AUTH_BASIC) {
|
||||
if (apply_basic_credential(buf, t, t->cred) < 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;
|
||||
if (apply_basic_credential(buf, t, t->url_cred) < 0) return -1;
|
||||
}
|
||||
|
||||
git_buf_puts(buf, "\r\n");
|
||||
@ -158,19 +380,32 @@ 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_authmechanism_t *auth_mechanism,
|
||||
char **auth_challenge)
|
||||
{
|
||||
unsigned i;
|
||||
char *entry;
|
||||
|
||||
git_vector_foreach(www_authenticate, i, entry) {
|
||||
if (!strncmp(entry, basic_authtype, 5) &&
|
||||
if (!strncmp(entry, negotiate_authtype, 9) &&
|
||||
(entry[9] == '\0' || entry[9] == ' ')) {
|
||||
*allowed_types |= GIT_CREDTYPE_DEFAULT;
|
||||
*auth_mechanism = GIT_HTTP_AUTH_NEGOTIATE;
|
||||
|
||||
*auth_challenge = git__strdup(entry);
|
||||
GITERR_CHECK_ALLOC(*auth_challenge);
|
||||
}
|
||||
|
||||
else if (!strncmp(entry, basic_authtype, 5) &&
|
||||
(entry[5] == '\0' || entry[5] == ' ')) {
|
||||
*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
|
||||
*auth_mechanism = GIT_HTTP_AUTH_BASIC;
|
||||
|
||||
*auth_challenge = git__strdup(entry);
|
||||
GITERR_CHECK_ALLOC(*auth_challenge);
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,7 +483,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 +491,31 @@ 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.)
|
||||
*/
|
||||
git__free(t->auth_challenge);
|
||||
t->auth_challenge = NULL;
|
||||
|
||||
if (parser->status_code == 401 &&
|
||||
get_verb == s->verb) {
|
||||
if (parse_authenticate_response(&t->www_authenticate,
|
||||
&allowed_auth_types,
|
||||
&t->auth_mechanism,
|
||||
&t->auth_challenge) < 0)
|
||||
return t->parse_error = PARSE_ERROR_GENERIC;
|
||||
|
||||
/* 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) {
|
||||
@ -511,10 +751,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);
|
||||
@ -613,10 +851,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);
|
||||
@ -688,10 +924,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;
|
||||
@ -855,6 +1089,24 @@ static int http_action(
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void clear_negotiate_state(http_subtransport *t)
|
||||
{
|
||||
#ifdef GIT_GSSAPI
|
||||
OM_uint32 status_minor;
|
||||
|
||||
if (t->negotiate_context != GSS_C_NO_CONTEXT) {
|
||||
gss_delete_sec_context(&status_minor, &t->negotiate_context, GSS_C_NO_BUFFER);
|
||||
t->negotiate_context = GSS_C_NO_CONTEXT;
|
||||
}
|
||||
|
||||
git_buf_free(&t->negotiate_target);
|
||||
|
||||
t->negotiate_configured = 0;
|
||||
t->negotiate_complete = 0;
|
||||
t->negotiate_oid = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int http_close(git_smart_subtransport *subtransport)
|
||||
{
|
||||
http_subtransport *t = (http_subtransport *) subtransport;
|
||||
@ -876,6 +1128,11 @@ static int http_close(git_smart_subtransport *subtransport)
|
||||
t->url_cred = NULL;
|
||||
}
|
||||
|
||||
git__free(t->auth_challenge);
|
||||
t->auth_challenge = NULL;
|
||||
|
||||
clear_negotiate_state(t);
|
||||
|
||||
gitno_connection_data_free_ptrs(&t->connection_data);
|
||||
|
||||
return 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user