summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2014-08-14 11:52:20 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2014-08-15 14:11:56 -0400
commit23135afa6f96ac63e65fd318918565222e46f65a (patch)
tree8c877ab8efbf959f5ade51e3bb26c47a40495a07
parent315cb38e1e32e77037e82c6cbdc383c151041112 (diff)
downloadlibgit2-23135afa6f96ac63e65fd318918565222e46f65a.tar.gz
Introduce proper http authentication API
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/transports/auth.c71
-rw-r--r--src/transports/auth.h63
-rw-r--r--src/transports/auth_negotiate.c275
-rw-r--r--src/transports/auth_negotiate.h27
-rw-r--r--src/transports/http.c374
6 files changed, 529 insertions, 283 deletions
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 <gssapi.h>
+#include <krb5.h>
+
+static gss_OID_desc negotiate_oid_spnego =
+ { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
+static gss_OID_desc negotiate_oid_krb5 =
+ { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+
+static gss_OID negotiate_oids[] =
+ { &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
+
+typedef struct {
+ git_http_auth_context parent;
+ unsigned configured : 1,
+ complete : 1;
+ git_buf target;
+ char *challenge;
+ gss_ctx_id_t gss_context;
+ gss_OID oid;
+} http_auth_negotiate_context;
+
+static void negotiate_err_set(
+ OM_uint32 status_major,
+ OM_uint32 status_minor,
+ const char *message)
+{
+ gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+ OM_uint32 status_display, context = 0;
+
+ if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
+ GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
+ giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
+ message, (int)buffer.length, (const char *)buffer.value,
+ status_major, status_minor);
+ gss_release_buffer(&status_minor, &buffer);
+ } else {
+ giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
+ message, status_major, status_minor);
+ }
+}
+
+static int negotiate_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+
+ assert(ctx && ctx->configured && challenge);
+
+ git__free(ctx->challenge);
+
+ ctx->challenge = git__strdup(challenge);
+ GITERR_CHECK_ALLOC(ctx->challenge);
+
+ return 0;
+}
+
+static int negotiate_next_token(
+ git_buf *buf,
+ git_http_auth_context *c,
+ git_cred *cred)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ OM_uint32 status_major, status_minor;
+ gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
+ input_token = GSS_C_EMPTY_BUFFER,
+ output_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
+ git_buf input_buf = GIT_BUF_INIT;
+ gss_name_t server = NULL;
+ gss_OID mech;
+ size_t challenge_len;
+ int error = 0;
+
+ assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
+
+ if (ctx->complete)
+ return 0;
+
+ target_buffer.value = (void *)ctx->target.ptr;
+ target_buffer.length = ctx->target.size;
+
+ status_major = gss_import_name(&status_minor, &target_buffer,
+ GSS_C_NT_HOSTBASED_SERVICE, &server);
+
+ if (GSS_ERROR(status_major)) {
+ negotiate_err_set(status_major, status_minor,
+ "Could not parse principal");
+ error = -1;
+ goto done;
+ }
+
+ challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
+
+ if (challenge_len < 9) {
+ giterr_set(GITERR_NET, "No negotiate challenge sent from server");
+ error = -1;
+ goto done;
+ } else if (challenge_len > 9) {
+ if (git_buf_decode_base64(&input_buf,
+ ctx->challenge + 10, challenge_len - 10) < 0) {
+ giterr_set(GITERR_NET, "Invalid negotiate challenge from server");
+ error = -1;
+ goto done;
+ }
+
+ input_token.value = input_buf.ptr;
+ input_token.length = input_buf.size;
+ input_token_ptr = &input_token;
+ } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ giterr_set(GITERR_NET, "Could not restart authentication");
+ error = -1;
+ goto done;
+ }
+
+ mech = &negotiate_oid_spnego;
+
+ if (GSS_ERROR(status_major = gss_init_sec_context(
+ &status_minor,
+ GSS_C_NO_CREDENTIAL,
+ &ctx->gss_context,
+ server,
+ mech,
+ GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ input_token_ptr,
+ NULL,
+ &output_token,
+ NULL,
+ NULL))) {
+ negotiate_err_set(status_major, status_minor, "Negotiate failure");
+ error = -1;
+ goto done;
+ }
+
+ /* This message merely told us auth was complete; we do not respond. */
+ if (status_major == GSS_S_COMPLETE) {
+ ctx->complete = 1;
+ goto done;
+ }
+
+ git_buf_puts(buf, "Authorization: Negotiate ");
+ git_buf_encode_base64(buf, output_token.value, output_token.length);
+ git_buf_puts(buf, "\r\n");
+
+ if (git_buf_oom(buf))
+ error = -1;
+
+done:
+ gss_release_name(&status_minor, &server);
+ gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
+ git_buf_free(&input_buf);
+ return error;
+}
+
+static void negotiate_context_free(git_http_auth_context *c)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ OM_uint32 status_minor;
+
+ if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(
+ &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ }
+
+ git_buf_free(&ctx->target);
+
+ git__free(ctx->challenge);
+
+ ctx->configured = 0;
+ ctx->complete = 0;
+ ctx->oid = NULL;
+
+ git__free(ctx);
+}
+
+static int negotiate_init_context(
+ http_auth_negotiate_context *ctx,
+ const gitno_connection_data *connection_data)
+{
+ OM_uint32 status_major, status_minor;
+ gss_OID item, *oid;
+ gss_OID_set mechanism_list;
+ size_t i;
+
+ /* Query supported mechanisms looking for SPNEGO) */
+ if (GSS_ERROR(status_major =
+ gss_indicate_mechs(&status_minor, &mechanism_list))) {
+ negotiate_err_set(status_major, status_minor,
+ "could not query mechanisms");
+ return -1;
+ }
+
+ if (mechanism_list) {
+ for (oid = negotiate_oids; *oid; oid++) {
+ for (i = 0; i < mechanism_list->count; i++) {
+ item = &mechanism_list->elements[i];
+
+ if (item->length == (*oid)->length &&
+ memcmp(item->elements, (*oid)->elements, item->length) == 0) {
+ ctx->oid = *oid;
+ break;
+ }
+
+ }
+
+ if (ctx->oid)
+ break;
+ }
+ }
+
+ gss_release_oid_set(&status_minor, &mechanism_list);
+
+ if (!ctx->oid) {
+ giterr_set(GITERR_NET, "Negotiate authentication is not supported");
+ return -1;
+ }
+
+ git_buf_puts(&ctx->target, "HTTP@");
+ git_buf_puts(&ctx->target, connection_data->host);
+
+ if (git_buf_oom(&ctx->target))
+ return -1;
+
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ ctx->configured = 1;
+
+ return 0;
+}
+
+int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data)
+{
+ http_auth_negotiate_context *ctx;
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
+ GITERR_CHECK_ALLOC(ctx);
+
+ if (negotiate_init_context(ctx, connection_data) < 0) {
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
+ ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
+ ctx->parent.set_challenge = negotiate_set_challenge;
+ ctx->parent.next_token = negotiate_next_token;
+ ctx->parent.free = negotiate_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ return 0;
+}
+
+#endif /* GIT_GSSAPI */
+
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 <gssapi.h>
-# include <krb5.h>
-#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);
}