|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit Service |
20376f |
*
|
|
Packit Service |
20376f |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit Service |
20376f |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#ifdef GIT_GSSAPI
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#include "git2.h"
|
|
Packit Service |
20376f |
#include "common.h"
|
|
Packit Service |
20376f |
#include "buffer.h"
|
|
Packit Service |
20376f |
#include "auth.h"
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#include <gssapi.h>
|
|
Packit Service |
20376f |
#include <krb5.h>
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static gss_OID_desc negotiate_oid_spnego =
|
|
Packit Service |
20376f |
{ 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
|
|
Packit Service |
20376f |
static gss_OID_desc negotiate_oid_krb5 =
|
|
Packit Service |
20376f |
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static gss_OID negotiate_oids[] =
|
|
Packit Service |
20376f |
{ &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
git_http_auth_context parent;
|
|
Packit Service |
20376f |
unsigned configured : 1,
|
|
Packit Service |
20376f |
complete : 1;
|
|
Packit Service |
20376f |
git_buf target;
|
|
Packit Service |
20376f |
char *challenge;
|
|
Packit Service |
20376f |
gss_ctx_id_t gss_context;
|
|
Packit Service |
20376f |
gss_OID oid;
|
|
Packit Service |
20376f |
} http_auth_negotiate_context;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void negotiate_err_set(
|
|
Packit Service |
20376f |
OM_uint32 status_major,
|
|
Packit Service |
20376f |
OM_uint32 status_minor,
|
|
Packit Service |
20376f |
const char *message)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
|
|
Packit Service |
20376f |
OM_uint32 status_display, context = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
|
|
Packit Service |
20376f |
GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
|
|
Packit Service |
20376f |
message, (int)buffer.length, (const char *)buffer.value,
|
|
Packit Service |
20376f |
status_major, status_minor);
|
|
Packit Service |
20376f |
gss_release_buffer(&status_minor, &buffer);
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
|
|
Packit Service |
20376f |
message, status_major, status_minor);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int negotiate_set_challenge(
|
|
Packit Service |
20376f |
git_http_auth_context *c,
|
|
Packit Service |
20376f |
const char *challenge)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(ctx && ctx->configured && challenge);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git__free(ctx->challenge);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
ctx->challenge = git__strdup(challenge);
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(ctx->challenge);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int negotiate_next_token(
|
|
Packit Service |
20376f |
git_buf *buf,
|
|
Packit Service |
20376f |
git_http_auth_context *c,
|
|
Packit Service |
20376f |
git_cred *cred)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
|
|
Packit Service |
20376f |
OM_uint32 status_major, status_minor;
|
|
Packit Service |
20376f |
gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
|
|
Packit Service |
20376f |
input_token = GSS_C_EMPTY_BUFFER,
|
|
Packit Service |
20376f |
output_token = GSS_C_EMPTY_BUFFER;
|
|
Packit Service |
20376f |
gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
|
|
Packit Service |
20376f |
git_buf input_buf = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
gss_name_t server = NULL;
|
|
Packit Service |
20376f |
gss_OID mech;
|
|
Packit Service |
20376f |
size_t challenge_len;
|
|
Packit Service |
20376f |
int error = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (ctx->complete)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
target_buffer.value = (void *)ctx->target.ptr;
|
|
Packit Service |
20376f |
target_buffer.length = ctx->target.size;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
status_major = gss_import_name(&status_minor, &target_buffer,
|
|
Packit Service |
20376f |
GSS_C_NT_HOSTBASED_SERVICE, &server);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (GSS_ERROR(status_major)) {
|
|
Packit Service |
20376f |
negotiate_err_set(status_major, status_minor,
|
|
Packit Service |
20376f |
"Could not parse principal");
|
|
Packit Service |
20376f |
error = -1;
|
|
Packit Service |
20376f |
goto done;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (challenge_len < 9) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "no negotiate challenge sent from server");
|
|
Packit Service |
20376f |
error = -1;
|
|
Packit Service |
20376f |
goto done;
|
|
Packit Service |
20376f |
} else if (challenge_len > 9) {
|
|
Packit Service |
20376f |
if (git_buf_decode_base64(&input_buf,
|
|
Packit Service |
20376f |
ctx->challenge + 10, challenge_len - 10) < 0) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "invalid negotiate challenge from server");
|
|
Packit Service |
20376f |
error = -1;
|
|
Packit Service |
20376f |
goto done;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
input_token.value = input_buf.ptr;
|
|
Packit Service |
20376f |
input_token.length = input_buf.size;
|
|
Packit Service |
20376f |
input_token_ptr = &input_token;
|
|
Packit Service |
20376f |
} else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "could not restart authentication");
|
|
Packit Service |
20376f |
error = -1;
|
|
Packit Service |
20376f |
goto done;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
mech = &negotiate_oid_spnego;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (GSS_ERROR(status_major = gss_init_sec_context(
|
|
Packit Service |
20376f |
&status_minor,
|
|
Packit Service |
20376f |
GSS_C_NO_CREDENTIAL,
|
|
Packit Service |
20376f |
&ctx->gss_context,
|
|
Packit Service |
20376f |
server,
|
|
Packit Service |
20376f |
mech,
|
|
Packit Service |
20376f |
GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
|
|
Packit Service |
20376f |
GSS_C_INDEFINITE,
|
|
Packit Service |
20376f |
GSS_C_NO_CHANNEL_BINDINGS,
|
|
Packit Service |
20376f |
input_token_ptr,
|
|
Packit Service |
20376f |
NULL,
|
|
Packit Service |
20376f |
&output_token,
|
|
Packit Service |
20376f |
NULL,
|
|
Packit Service |
20376f |
NULL))) {
|
|
Packit Service |
20376f |
negotiate_err_set(status_major, status_minor, "Negotiate failure");
|
|
Packit Service |
20376f |
error = -1;
|
|
Packit Service |
20376f |
goto done;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* This message merely told us auth was complete; we do not respond. */
|
|
Packit Service |
20376f |
if (status_major == GSS_S_COMPLETE) {
|
|
Packit Service |
20376f |
ctx->complete = 1;
|
|
Packit Service |
20376f |
goto done;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_puts(buf, "Authorization: Negotiate ");
|
|
Packit Service |
20376f |
git_buf_encode_base64(buf, output_token.value, output_token.length);
|
|
Packit Service |
20376f |
git_buf_puts(buf, "\r\n");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_oom(buf))
|
|
Packit Service |
20376f |
error = -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
done:
|
|
Packit Service |
20376f |
gss_release_name(&status_minor, &server);
|
|
Packit Service |
20376f |
gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
|
|
Packit Service |
20376f |
git_buf_free(&input_buf);
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void negotiate_context_free(git_http_auth_context *c)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
|
|
Packit Service |
20376f |
OM_uint32 status_minor;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (ctx->gss_context != GSS_C_NO_CONTEXT) {
|
|
Packit Service |
20376f |
gss_delete_sec_context(
|
|
Packit Service |
20376f |
&status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
|
|
Packit Service |
20376f |
ctx->gss_context = GSS_C_NO_CONTEXT;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&ctx->target);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git__free(ctx->challenge);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
ctx->configured = 0;
|
|
Packit Service |
20376f |
ctx->complete = 0;
|
|
Packit Service |
20376f |
ctx->oid = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git__free(ctx);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int negotiate_init_context(
|
|
Packit Service |
20376f |
http_auth_negotiate_context *ctx,
|
|
Packit Service |
20376f |
const gitno_connection_data *connection_data)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
OM_uint32 status_major, status_minor;
|
|
Packit Service |
20376f |
gss_OID item, *oid;
|
|
Packit Service |
20376f |
gss_OID_set mechanism_list;
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Query supported mechanisms looking for SPNEGO) */
|
|
Packit Service |
20376f |
if (GSS_ERROR(status_major =
|
|
Packit Service |
20376f |
gss_indicate_mechs(&status_minor, &mechanism_list))) {
|
|
Packit Service |
20376f |
negotiate_err_set(status_major, status_minor,
|
|
Packit Service |
20376f |
"could not query mechanisms");
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (mechanism_list) {
|
|
Packit Service |
20376f |
for (oid = negotiate_oids; *oid; oid++) {
|
|
Packit Service |
20376f |
for (i = 0; i < mechanism_list->count; i++) {
|
|
Packit Service |
20376f |
item = &mechanism_list->elements[i];
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (item->length == (*oid)->length &&
|
|
Packit Service |
20376f |
memcmp(item->elements, (*oid)->elements, item->length) == 0) {
|
|
Packit Service |
20376f |
ctx->oid = *oid;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (ctx->oid)
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
gss_release_oid_set(&status_minor, &mechanism_list);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!ctx->oid) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "negotiate authentication is not supported");
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_puts(&ctx->target, "HTTP@");
|
|
Packit Service |
20376f |
git_buf_puts(&ctx->target, connection_data->host);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_oom(&ctx->target))
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
ctx->gss_context = GSS_C_NO_CONTEXT;
|
|
Packit Service |
20376f |
ctx->configured = 1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_http_auth_negotiate(
|
|
Packit Service |
20376f |
git_http_auth_context **out,
|
|
Packit Service |
20376f |
const gitno_connection_data *connection_data)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_auth_negotiate_context *ctx;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*out = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(ctx);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (negotiate_init_context(ctx, connection_data) < 0) {
|
|
Packit Service |
20376f |
git__free(ctx);
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
|
|
Packit Service |
20376f |
ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
|
|
Packit Service |
20376f |
ctx->parent.set_challenge = negotiate_set_challenge;
|
|
Packit Service |
20376f |
ctx->parent.next_token = negotiate_next_token;
|
|
Packit Service |
20376f |
ctx->parent.free = negotiate_context_free;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*out = (git_http_auth_context *)ctx;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#endif /* GIT_GSSAPI */
|
|
Packit Service |
20376f |
|