Commit e1c44290 by Vicent Marti

Merge pull request #2497 from ethomson/kerberos3

SPNEGO authentication via GSSAPI
parents fb48a51c 23135afa
......@@ -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" OFF )
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)
......
# - 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)
......@@ -189,10 +189,10 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
static const char b64str[] =
static const char base64_encode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
{
size_t extra = len % 3;
uint8_t *write, a, b, c;
......@@ -207,19 +207,19 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
b = *read++;
c = *read++;
*write++ = b64str[a >> 2];
*write++ = b64str[(a & 0x03) << 4 | b >> 4];
*write++ = b64str[(b & 0x0f) << 2 | c >> 6];
*write++ = b64str[c & 0x3f];
*write++ = base64_encode[a >> 2];
*write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
*write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
*write++ = base64_encode[c & 0x3f];
}
if (extra > 0) {
a = *read++;
b = (extra > 1) ? *read++ : 0;
*write++ = b64str[a >> 2];
*write++ = b64str[(a & 0x03) << 4 | b >> 4];
*write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
*write++ = base64_encode[a >> 2];
*write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
*write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
*write++ = '=';
}
......@@ -229,10 +229,56 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
return 0;
}
/* The inverse of base64_encode, offset by '+' == 43. */
static const int8_t base64_decode[] = {
62,
-1, -1, -1,
63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-1, -1, -1, 0, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-1, -1, -1, -1, -1, -1,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
{
size_t i;
int8_t a, b, c, d;
size_t orig_size = buf->size;
assert(len % 4 == 0);
ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1);
for (i = 0; i < len; i += 4) {
if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
(b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
(c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
(d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
buf->size = orig_size;
buf->ptr[buf->size] = '\0';
giterr_set(GITERR_INVALID, "Invalid base64 input");
return -1;
}
buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
}
buf->ptr[buf->size] = '\0';
return 0;
}
static const char b85str[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
int git_buf_put_base85(git_buf *buf, const char *data, size_t len)
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
......
......@@ -156,10 +156,12 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Write data as base64 encoded in buffer */
int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
/* Decode the given bas64 and write the result to the buffer */
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
/* Write data as "base85" encoded in buffer */
int git_buf_put_base85(git_buf *buf, const char *data, size_t len);
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
/*
* Insert, remove or replace a portion of the buffer.
......
......@@ -352,7 +352,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
else
git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
git_buf_put_base85(pi->buf, scan, chunk_len);
git_buf_encode_base85(pi->buf, scan, chunk_len);
git_buf_putc(pi->buf, '\n');
if (git_buf_oom(pi->buf)) {
......
/*
* 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;
}
/*
* 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
/*
* 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 */
/*
* 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
......@@ -101,7 +101,7 @@ static int apply_basic_credential(HINTERNET request, git_cred *cred)
if (git_buf_oom(&raw) ||
git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
goto on_error;
if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
......
......@@ -748,7 +748,7 @@ void test_core_buffer__unescape(void)
assert_unescape("", "");
}
void test_core_buffer__base64(void)
void test_core_buffer__encode_base64(void)
{
git_buf buf = GIT_BUF_INIT;
......@@ -759,33 +759,54 @@ void test_core_buffer__base64(void)
* 0x 1d 06 21 29 1c 30
* d G h p c w
*/
cl_git_pass(git_buf_put_base64(&buf, "this", 4));
cl_git_pass(git_buf_encode_base64(&buf, "this", 4));
cl_assert_equal_s("dGhpcw==", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_buf_put_base64(&buf, "this!", 5));
cl_git_pass(git_buf_encode_base64(&buf, "this!", 5));
cl_assert_equal_s("dGhpcyE=", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6));
cl_git_pass(git_buf_encode_base64(&buf, "this!\n", 6));
cl_assert_equal_s("dGhpcyEK", buf.ptr);
git_buf_free(&buf);
}
void test_core_buffer__base85(void)
void test_core_buffer__decode_base64(void)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_put_base85(&buf, "this", 4));
cl_git_pass(git_buf_decode_base64(&buf, "dGhpcw==", 8));
cl_assert_equal_s("this", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyE=", 8));
cl_assert_equal_s("this!", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyEK", 8));
cl_assert_equal_s("this!\n", buf.ptr);
cl_git_fail(git_buf_decode_base64(&buf, "This is not a valid base64 string!!!", 36));
cl_assert_equal_s("this!\n", buf.ptr);
git_buf_free(&buf);
}
void test_core_buffer__encode_base85(void)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_encode_base85(&buf, "this", 4));
cl_assert_equal_s("bZBXF", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_buf_put_base85(&buf, "two rnds", 8));
cl_git_pass(git_buf_encode_base85(&buf, "two rnds", 8));
cl_assert_equal_s("ba!tca&BaE", buf.ptr);
git_buf_clear(&buf);
cl_git_pass(git_buf_put_base85(&buf, "this is base 85 encoded",
cl_git_pass(git_buf_encode_base85(&buf, "this is base 85 encoded",
strlen("this is base 85 encoded")));
cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr);
git_buf_clear(&buf);
......
......@@ -226,9 +226,28 @@ void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
cl_git_fail_with(git_clone(&g_repo, remote_url, "./foo", &g_options), -1);
}
int cred_default(
git_cred **cred,
const char *url,
const char *user_from_url,
unsigned int allowed_types,
void *payload)
{
GIT_UNUSED(url);
GIT_UNUSED(user_from_url);
GIT_UNUSED(payload);
if (!(allowed_types & GIT_CREDTYPE_DEFAULT))
return 0;
return git_cred_default_new(cred);
}
void test_online_clone__credentials(void)
{
/* Remote URL environment variable must be set. User and password are optional. */
/* Remote URL environment variable must be set.
* User and password are optional.
*/
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
git_cred_userpass_payload user_pass = {
cl_getenv("GITTEST_REMOTE_USER"),
......@@ -237,8 +256,12 @@ void test_online_clone__credentials(void)
if (!remote_url) return;
g_options.remote_callbacks.credentials = git_cred_userpass;
g_options.remote_callbacks.payload = &user_pass;
if (cl_getenv("GITTEST_REMOTE_DEFAULT")) {
g_options.remote_callbacks.credentials = cred_default;
} else {
g_options.remote_callbacks.credentials = git_cred_userpass;
g_options.remote_callbacks.payload = &user_pass;
}
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
......@@ -346,7 +369,7 @@ void test_online_clone__ssh_with_paths(void)
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
if (!remote_url || !remote_user)
if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0)
clar__skip();
g_options.remote_cb = custom_remote_ssh_with_paths;
......
......@@ -204,6 +204,8 @@ static void verify_tracking_branches(git_remote *remote, expected_ref expected_r
cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
git_reference_free(ref);
}
cl_assert_equal_i(error, GIT_ITEROVER);
......@@ -852,6 +854,9 @@ void test_online_push__notes(void)
const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
push_status exp_stats[] = { { "refs/notes/commits", 1 } };
expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
const char *specs_del[] = { ":refs/notes/commits" };
expected_ref exp_refs_del[] = { };
git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
target_oid = &_oid_b6;
......@@ -864,5 +869,11 @@ void test_online_push__notes(void)
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
/* And make sure to delete the note */
do_push(specs_del, ARRAY_SIZE(specs_del),
exp_stats, 1,
exp_refs_del, ARRAY_SIZE(exp_refs_del), 0, 0, 0);
git_signature_free(signature);
}
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