From 23135afa6f96ac63e65fd318918565222e46f65a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Aug 2014 11:52:20 -0500 Subject: Introduce proper http authentication API --- CMakeLists.txt | 2 +- src/transports/auth.c | 71 ++++++++ src/transports/auth.h | 63 +++++++ src/transports/auth_negotiate.c | 275 +++++++++++++++++++++++++++++ src/transports/auth_negotiate.h | 27 +++ src/transports/http.c | 374 ++++++++++------------------------------ 6 files changed, 529 insertions(+), 283 deletions(-) create mode 100644 src/transports/auth.c create mode 100644 src/transports/auth.h create mode 100644 src/transports/auth_negotiate.c create mode 100644 src/transports/auth_negotiate.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 54b0c8af1..76cca4f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +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( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) OPTION( VALGRIND "Configure build for valgrind" OFF ) IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/src/transports/auth.c b/src/transports/auth.c new file mode 100644 index 000000000..c1154db34 --- /dev/null +++ b/src/transports/auth.c @@ -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; +} + diff --git a/src/transports/auth.h b/src/transports/auth.h new file mode 100644 index 000000000..52138cf8f --- /dev/null +++ b/src/transports/auth.h @@ -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 + diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c new file mode 100644 index 000000000..8b99fc735 --- /dev/null +++ b/src/transports/auth_negotiate.c @@ -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 +#include + +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 */ + diff --git a/src/transports/auth_negotiate.h b/src/transports/auth_negotiate.h new file mode 100644 index 000000000..d7270b7ab --- /dev/null +++ b/src/transports/auth_negotiate.h @@ -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 + diff --git a/src/transports/http.c b/src/transports/http.c index c43f6c548..5c5e5d391 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -11,11 +11,13 @@ #include "buffer.h" #include "netops.h" #include "smart.h" +#include "auth.h" +#include "auth_negotiate.h" -#ifdef GIT_GSSAPI -# include -# include -#endif +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"; @@ -25,18 +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"; -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) @@ -51,11 +41,6 @@ enum last_cb { VALUE }; -typedef enum { - GIT_HTTP_AUTH_BASIC = 1, - GIT_HTTP_AUTH_NEGOTIATE = 2, -} http_authmechanism_t; - typedef struct { git_smart_subtransport_stream parent; const char *service; @@ -75,10 +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; - char *auth_challenge; bool connected; /* Parser structures */ @@ -95,13 +76,10 @@ typedef struct { 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 + /* Authentication */ + git_cred *cred; + git_cred *url_cred; + git_vector auth_contexts; } http_subtransport; typedef struct { @@ -114,217 +92,93 @@ typedef struct { size_t *bytes_read; } parser_context; -static int apply_basic_credential( - git_buf *buf, - http_subtransport *transport, - 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; - - GIT_UNUSED(transport); - - git_buf_printf(&raw, "%s:%s", c->username, c->password); + unsigned int credtype = *(unsigned int *)data; - if (git_buf_oom(&raw) || - git_buf_puts(buf, "Authorization: Basic ") < 0 || - git_buf_encode_base64(buf, git_buf_cstr(&raw), raw.size) < 0 || - git_buf_puts(buf, "\r\n") < 0) - goto on_error; - - error = 0; - -on_error: - if (raw.size) - memset(raw.ptr, 0x0, raw.size); - - git_buf_free(&raw); - return error; + return !!(scheme->credtypes & credtype); } -#ifdef GIT_GSSAPI - -static void negotiate_err_set( - OM_uint32 status_major, - OM_uint32 status_minor, - const char *message) +static bool challenge_match(git_http_auth_scheme *scheme, void *data) { - 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); - } + const char *scheme_name = scheme->name; + const char *challenge = (const char *)data; + size_t scheme_len; + + scheme_len = strlen(scheme_name); + return (strncmp(challenge, scheme_name, scheme_len) == 0 && + (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' ')); } -static int negotiate_configure(http_subtransport *transport) +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) { - OM_uint32 status_major, status_minor; - gss_OID item, *oid; - gss_OID_set mechanism_list; + git_http_auth_scheme *scheme = NULL; + git_http_auth_context *context = NULL, *c; 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; - } - - } + *out = NULL; - if (transport->negotiate_oid) - break; + for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) { + if (scheme_match(&auth_schemes[i], data)) { + scheme = &auth_schemes[i]; + break; } } - gss_release_oid_set(&status_minor, &mechanism_list); + if (!scheme) + return 0; - if (!transport->negotiate_oid) { - giterr_set(GITERR_NET, "Negotiate authentication is not supported"); - return -1; + /* 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; + } } - git_buf_puts(&transport->negotiate_target, "HTTP@"); - git_buf_puts(&transport->negotiate_target, transport->connection_data.host); + 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; + } - if (git_buf_oom(&transport->negotiate_target)) - return -1; + *out = context; - 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) +static int apply_credentials(git_buf *buf, http_subtransport *t) { - 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; - } + 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; - /* This message merely told us auth was complete; we do not respond. */ - if (status_major == GSS_S_COMPLETE) { - transport->negotiate_complete = 1; - goto done; + cred = t->url_cred; } - 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; -} + if (!cred) + return 0; -static int apply_negotiate_credential( - git_buf *buf, - http_subtransport *transport, - git_cred *cred) -{ - if (!transport->negotiate_configured && negotiate_configure(transport) < 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; - if (transport->negotiate_complete) - return 0; - - return negotiate_next_token(buf, transport, cred); + return context->next_token(buf, context, cred); } -#endif /* GIT_GSSAPI */ - static int gen_request( git_buf *buf, http_stream *s, @@ -350,27 +204,8 @@ 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) { - 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, t->url_cred) < 0) return -1; - } + if (apply_credentials(buf, t) < 0) + return -1; git_buf_puts(buf, "\r\n"); @@ -382,31 +217,24 @@ static int gen_request( static int parse_authenticate_response( git_vector *www_authenticate, - int *allowed_types, - http_authmechanism_t *auth_mechanism, - char **auth_challenge) + http_subtransport *t, + int *allowed_types) { - unsigned i; - char *entry; - - git_vector_foreach(www_authenticate, i, entry) { - if (!strncmp(entry, negotiate_authtype, 9) && - (entry[9] == '\0' || entry[9] == ' ')) { - *allowed_types |= GIT_CREDTYPE_DEFAULT; - *auth_mechanism = GIT_HTTP_AUTH_NEGOTIATE; + git_http_auth_context *context; + char *challenge; + size_t i; - *auth_challenge = git__strdup(entry); - GITERR_CHECK_ALLOC(*auth_challenge); - } + git_vector_foreach(www_authenticate, i, challenge) { + if (auth_context_match(&context, t, challenge_match, challenge) < 0) + return -1; + else if (!context) + continue; - else if (!strncmp(entry, basic_authtype, 5) && - (entry[5] == '\0' || entry[5] == ' ')) { - *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; - *auth_mechanism = GIT_HTTP_AUTH_BASIC; + if (context->set_challenge && + context->set_challenge(context, challenge) < 0) + return -1; - *auth_challenge = git__strdup(entry); - GITERR_CHECK_ALLOC(*auth_challenge); - } + *allowed_types |= context->credtypes; } return 0; @@ -495,13 +323,8 @@ static int on_headers_complete(http_parser *parser) * is not complete) or a 200 (simply informing us that auth *is* * complete.) */ - git__free(t->auth_challenge); - t->auth_challenge = NULL; - - if (parse_authenticate_response(&t->www_authenticate, - &allowed_auth_types, - &t->auth_mechanism, - &t->auth_challenge) < 0) + if (parse_authenticate_response(&t->www_authenticate, t, + &allowed_auth_types) < 0) return t->parse_error = PARSE_ERROR_GENERIC; /* Check for an authentication failure. */ @@ -1089,27 +912,11 @@ 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; + git_http_auth_context *context; + size_t i; clear_parser_state(t); @@ -1128,10 +935,12 @@ static int http_close(git_smart_subtransport *subtransport) t->url_cred = NULL; } - git__free(t->auth_challenge); - t->auth_challenge = NULL; + git_vector_foreach(&t->auth_contexts, i, context) { + if (context->free) + context->free(context); + } - clear_negotiate_state(t); + git_vector_clear(&t->auth_contexts); gitno_connection_data_free_ptrs(&t->connection_data); @@ -1144,6 +953,7 @@ static void http_free(git_smart_subtransport *subtransport) http_close(subtransport); + git_vector_free(&t->auth_contexts); git__free(t); } -- cgit v1.2.1