Commit 315cb38e by Edward Thomson

Add GSSAPI support for SPNEGO/Kerberos auth over HTTP

parent e003f83a
...@@ -36,6 +36,7 @@ OPTION( ANDROID "Build for android NDK" OFF ) ...@@ -36,6 +36,7 @@ OPTION( ANDROID "Build for android NDK" OFF )
OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF )
OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) 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 ) OPTION( VALGRIND "Configure build for valgrind" OFF )
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
...@@ -208,6 +209,14 @@ IF (LIBSSH2_FOUND) ...@@ -208,6 +209,14 @@ IF (LIBSSH2_FOUND)
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
ENDIF() ENDIF()
# Optional external dependency: libgssapi
IF (USE_GSSAPI)
FIND_PACKAGE(GSSAPI)
ENDIF()
IF (GSSAPI_FOUND)
ADD_DEFINITIONS(-DGIT_GSSAPI)
ENDIF()
# Optional external dependency: iconv # Optional external dependency: iconv
IF (USE_ICONV) IF (USE_ICONV)
FIND_PACKAGE(Iconv) FIND_PACKAGE(Iconv)
...@@ -387,6 +396,7 @@ ENDIF() ...@@ -387,6 +396,7 @@ ENDIF()
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) 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 ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES}) TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES}) TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(git2) TARGET_OS_LIBRARIES(git2)
...@@ -453,6 +463,7 @@ IF (BUILD_CLAR) ...@@ -453,6 +463,7 @@ IF (BUILD_CLAR)
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(libgit2_clar) TARGET_OS_LIBRARIES(libgit2_clar)
MSVC_SPLIT_SOURCES(libgit2_clar) MSVC_SPLIT_SOURCES(libgit2_clar)
......
# - 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 @@ ...@@ -12,6 +12,11 @@
#include "netops.h" #include "netops.h"
#include "smart.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_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-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"; static const char *upload_pack_service_url = "/git-upload-pack";
...@@ -21,6 +26,17 @@ static const char *receive_pack_service_url = "/git-receive-pack"; ...@@ -21,6 +26,17 @@ static const char *receive_pack_service_url = "/git-receive-pack";
static const char *get_verb = "GET"; static const char *get_verb = "GET";
static const char *post_verb = "POST"; static const char *post_verb = "POST";
static const char *basic_authtype = "Basic"; 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) #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
...@@ -37,6 +53,7 @@ enum last_cb { ...@@ -37,6 +53,7 @@ enum last_cb {
typedef enum { typedef enum {
GIT_HTTP_AUTH_BASIC = 1, GIT_HTTP_AUTH_BASIC = 1,
GIT_HTTP_AUTH_NEGOTIATE = 2,
} http_authmechanism_t; } http_authmechanism_t;
typedef struct { typedef struct {
...@@ -61,6 +78,7 @@ typedef struct { ...@@ -61,6 +78,7 @@ typedef struct {
git_cred *cred; git_cred *cred;
git_cred *url_cred; git_cred *url_cred;
http_authmechanism_t auth_mechanism; http_authmechanism_t auth_mechanism;
char *auth_challenge;
bool connected; bool connected;
/* Parser structures */ /* Parser structures */
...@@ -76,6 +94,14 @@ typedef struct { ...@@ -76,6 +94,14 @@ typedef struct {
enum last_cb last_cb; enum last_cb last_cb;
int parse_error; int parse_error;
unsigned parse_finished : 1; 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; } http_subtransport;
typedef struct { typedef struct {
...@@ -88,12 +114,17 @@ typedef struct { ...@@ -88,12 +114,17 @@ typedef struct {
size_t *bytes_read; size_t *bytes_read;
} parser_context; } 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_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
git_buf raw = GIT_BUF_INIT; git_buf raw = GIT_BUF_INIT;
int error = -1; int error = -1;
GIT_UNUSED(transport);
git_buf_printf(&raw, "%s:%s", c->username, c->password); git_buf_printf(&raw, "%s:%s", c->username, c->password);
if (git_buf_oom(&raw) || if (git_buf_oom(&raw) ||
...@@ -112,6 +143,188 @@ on_error: ...@@ -112,6 +143,188 @@ on_error:
return 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( static int gen_request(
git_buf *buf, git_buf *buf,
http_stream *s, http_stream *s,
...@@ -137,17 +350,26 @@ static int gen_request( ...@@ -137,17 +350,26 @@ static int gen_request(
git_buf_puts(buf, "Accept: */*\r\n"); git_buf_puts(buf, "Accept: */*\r\n");
/* Apply credentials to the request */ /* 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 && if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
t->auth_mechanism == GIT_HTTP_AUTH_BASIC && t->auth_mechanism == GIT_HTTP_AUTH_BASIC) {
apply_basic_credential(buf, t->cred) < 0) if (apply_basic_credential(buf, t, t->cred) < 0)
return -1; return -1;
}
/* Use url-parsed basic auth if username and password are both provided */ /* 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->cred && t->connection_data.user && t->connection_data.pass) {
if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred, if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
t->connection_data.user, t->connection_data.pass) < 0) t->connection_data.user, t->connection_data.pass) < 0)
return -1; 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"); git_buf_puts(buf, "\r\n");
...@@ -158,19 +380,32 @@ static int gen_request( ...@@ -158,19 +380,32 @@ static int gen_request(
return 0; return 0;
} }
static int parse_unauthorized_response( static int parse_authenticate_response(
git_vector *www_authenticate, git_vector *www_authenticate,
int *allowed_types, int *allowed_types,
http_authmechanism_t *auth_mechanism) http_authmechanism_t *auth_mechanism,
char **auth_challenge)
{ {
unsigned i; unsigned i;
char *entry; char *entry;
git_vector_foreach(www_authenticate, i, 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] == ' ')) { (entry[5] == '\0' || entry[5] == ' ')) {
*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
*auth_mechanism = GIT_HTTP_AUTH_BASIC; *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) ...@@ -248,7 +483,7 @@ static int on_headers_complete(http_parser *parser)
http_subtransport *t = ctx->t; http_subtransport *t = ctx->t;
http_stream *s = ctx->s; http_stream *s = ctx->s;
git_buf buf = GIT_BUF_INIT; 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 /* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */ * and ready for consumption. */
...@@ -256,26 +491,31 @@ static int on_headers_complete(http_parser *parser) ...@@ -256,26 +491,31 @@ static int on_headers_complete(http_parser *parser)
if (on_header_ready(t) < 0) if (on_header_ready(t) < 0)
return t->parse_error = PARSE_ERROR_GENERIC; 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 && if (parse_authenticate_response(&t->www_authenticate,
get_verb == s->verb) { &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) { if (!t->owner->cred_acquire_cb) {
no_callback = 1; no_callback = 1;
} else { } else {
int allowed_types = 0; if (allowed_auth_types &&
(!t->cred || 0 == (t->cred->credtype & allowed_auth_types))) {
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))) {
error = t->owner->cred_acquire_cb(&t->cred, error = t->owner->cred_acquire_cb(&t->cred,
t->owner->url, t->owner->url,
t->connection_data.user, t->connection_data.user,
allowed_types, allowed_auth_types,
t->owner->cred_acquire_payload); t->owner->cred_acquire_payload);
if (error == GIT_PASSTHROUGH) { if (error == GIT_PASSTHROUGH) {
...@@ -511,10 +751,8 @@ replay: ...@@ -511,10 +751,8 @@ replay:
clear_parser_state(t); clear_parser_state(t);
if (gen_request(&request, s, 0) < 0) { if (gen_request(&request, s, 0) < 0)
giterr_set(GITERR_NET, "Failed to generate request");
return -1; return -1;
}
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request); git_buf_free(&request);
...@@ -613,10 +851,8 @@ static int http_stream_write_chunked( ...@@ -613,10 +851,8 @@ static int http_stream_write_chunked(
clear_parser_state(t); clear_parser_state(t);
if (gen_request(&request, s, 0) < 0) { if (gen_request(&request, s, 0) < 0)
giterr_set(GITERR_NET, "Failed to generate request");
return -1; return -1;
}
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request); git_buf_free(&request);
...@@ -688,10 +924,8 @@ static int http_stream_write_single( ...@@ -688,10 +924,8 @@ static int http_stream_write_single(
clear_parser_state(t); clear_parser_state(t);
if (gen_request(&request, s, len) < 0) { if (gen_request(&request, s, len) < 0)
giterr_set(GITERR_NET, "Failed to generate request");
return -1; return -1;
}
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
goto on_error; goto on_error;
...@@ -855,6 +1089,24 @@ static int http_action( ...@@ -855,6 +1089,24 @@ static int http_action(
return -1; 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) static int http_close(git_smart_subtransport *subtransport)
{ {
http_subtransport *t = (http_subtransport *) subtransport; http_subtransport *t = (http_subtransport *) subtransport;
...@@ -876,6 +1128,11 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -876,6 +1128,11 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL; 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); gitno_connection_data_free_ptrs(&t->connection_data);
return 0; return 0;
......
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