diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2014-07-31 18:43:20 -0400 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2014-08-15 11:12:42 -0400 |
commit | 315cb38e1e32e77037e82c6cbdc383c151041112 (patch) | |
tree | ef6f113d3df5acfe713a1c0b26b2e3234c7c8870 | |
parent | e003f83a5840d6e9966f1830768771bcd205ba52 (diff) | |
download | libgit2-315cb38e1e32e77037e82c6cbdc383c151041112.tar.gz |
Add GSSAPI support for SPNEGO/Kerberos auth over HTTP
-rw-r--r-- | CMakeLists.txt | 11 | ||||
-rw-r--r-- | cmake/Modules/FindGSSAPI.cmake | 324 | ||||
-rw-r--r-- | src/transports/http.c | 317 |
3 files changed, 622 insertions, 30 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f1a97edb..54b0c8af1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/Modules/FindGSSAPI.cmake b/cmake/Modules/FindGSSAPI.cmake new file mode 100644 index 000000000..8520d35df --- /dev/null +++ b/cmake/Modules/FindGSSAPI.cmake @@ -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) diff --git a/src/transports/http.c b/src/transports/http.c index ffa293ec0..c43f6c548 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -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; |