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 ) ...@@ -36,6 +36,7 @@ OPTION( ANDROID "Build for android NDK" OFF )
OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF )
OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) 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 ) OPTION( VALGRIND "Configure build for valgrind" OFF )
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
...@@ -208,6 +209,14 @@ IF (LIBSSH2_FOUND) ...@@ -208,6 +209,14 @@ IF (LIBSSH2_FOUND)
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
ENDIF() ENDIF()
# Optional external dependency: libgssapi
IF (USE_GSSAPI)
FIND_PACKAGE(GSSAPI)
ENDIF()
IF (GSSAPI_FOUND)
ADD_DEFINITIONS(-DGIT_GSSAPI)
ENDIF()
# Optional external dependency: iconv # Optional external dependency: iconv
IF (USE_ICONV) IF (USE_ICONV)
FIND_PACKAGE(Iconv) FIND_PACKAGE(Iconv)
...@@ -387,6 +396,7 @@ ENDIF() ...@@ -387,6 +396,7 @@ ENDIF()
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) 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 ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES}) TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES}) TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(git2) TARGET_OS_LIBRARIES(git2)
...@@ -453,6 +463,7 @@ IF (BUILD_CLAR) ...@@ -453,6 +463,7 @@ IF (BUILD_CLAR)
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(libgit2_clar) TARGET_OS_LIBRARIES(libgit2_clar)
MSVC_SPLIT_SOURCES(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) ...@@ -189,10 +189,10 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string)); return git_buf_put(buf, string, strlen(string));
} }
static const char b64str[] = static const char base64_encode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; "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; size_t extra = len % 3;
uint8_t *write, a, b, c; uint8_t *write, a, b, c;
...@@ -207,19 +207,19 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len) ...@@ -207,19 +207,19 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
b = *read++; b = *read++;
c = *read++; c = *read++;
*write++ = b64str[a >> 2]; *write++ = base64_encode[a >> 2];
*write++ = b64str[(a & 0x03) << 4 | b >> 4]; *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
*write++ = b64str[(b & 0x0f) << 2 | c >> 6]; *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
*write++ = b64str[c & 0x3f]; *write++ = base64_encode[c & 0x3f];
} }
if (extra > 0) { if (extra > 0) {
a = *read++; a = *read++;
b = (extra > 1) ? *read++ : 0; b = (extra > 1) ? *read++ : 0;
*write++ = b64str[a >> 2]; *write++ = base64_encode[a >> 2];
*write++ = b64str[(a & 0x03) << 4 | b >> 4]; *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
*write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '='; *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
*write++ = '='; *write++ = '=';
} }
...@@ -229,10 +229,56 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len) ...@@ -229,10 +229,56 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
return 0; 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[] = static const char b85str[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; "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); ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
......
...@@ -156,10 +156,12 @@ void git_buf_rtrim(git_buf *buf); ...@@ -156,10 +156,12 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b); int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Write data as base64 encoded in buffer */ /* 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 */ /* 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. * 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) ...@@ -352,7 +352,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
else else
git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); 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'); git_buf_putc(pi->buf, '\n');
if (git_buf_oom(pi->buf)) { 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
...@@ -11,6 +11,13 @@ ...@@ -11,6 +11,13 @@
#include "buffer.h" #include "buffer.h"
#include "netops.h" #include "netops.h"
#include "smart.h" #include "smart.h"
#include "auth.h"
#include "auth_negotiate.h"
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_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack"; static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
...@@ -20,7 +27,6 @@ static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive ...@@ -20,7 +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 *receive_pack_service_url = "/git-receive-pack";
static const char *get_verb = "GET"; static const char *get_verb = "GET";
static const char *post_verb = "POST"; static const char *post_verb = "POST";
static const char *basic_authtype = "Basic";
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport) #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
...@@ -35,10 +41,6 @@ enum last_cb { ...@@ -35,10 +41,6 @@ enum last_cb {
VALUE VALUE
}; };
typedef enum {
GIT_HTTP_AUTH_BASIC = 1,
} http_authmechanism_t;
typedef struct { typedef struct {
git_smart_subtransport_stream parent; git_smart_subtransport_stream parent;
const char *service; const char *service;
...@@ -58,9 +60,6 @@ typedef struct { ...@@ -58,9 +60,6 @@ typedef struct {
transport_smart *owner; transport_smart *owner;
gitno_socket socket; gitno_socket socket;
gitno_connection_data connection_data; gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
http_authmechanism_t auth_mechanism;
bool connected; bool connected;
/* Parser structures */ /* Parser structures */
...@@ -76,6 +75,11 @@ typedef struct { ...@@ -76,6 +75,11 @@ typedef struct {
enum last_cb last_cb; enum last_cb last_cb;
int parse_error; int parse_error;
unsigned parse_finished : 1; unsigned parse_finished : 1;
/* Authentication */
git_cred *cred;
git_cred *url_cred;
git_vector auth_contexts;
} http_subtransport; } http_subtransport;
typedef struct { typedef struct {
...@@ -88,28 +92,91 @@ typedef struct { ...@@ -88,28 +92,91 @@ typedef struct {
size_t *bytes_read; size_t *bytes_read;
} parser_context; } parser_context;
static int apply_basic_credential(git_buf *buf, git_cred *cred) static bool credtype_match(git_http_auth_scheme *scheme, void *data)
{
unsigned int credtype = *(unsigned int *)data;
return !!(scheme->credtypes & credtype);
}
static bool challenge_match(git_http_auth_scheme *scheme, void *data)
{ {
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; const char *scheme_name = scheme->name;
git_buf raw = GIT_BUF_INIT; const char *challenge = (const char *)data;
int error = -1; size_t scheme_len;
git_buf_printf(&raw, "%s:%s", c->username, c->password); scheme_len = strlen(scheme_name);
return (strncmp(challenge, scheme_name, scheme_len) == 0 &&
(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
}
if (git_buf_oom(&raw) || static int auth_context_match(
git_buf_puts(buf, "Authorization: Basic ") < 0 || git_http_auth_context **out,
git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 || http_subtransport *t,
git_buf_puts(buf, "\r\n") < 0) bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
goto on_error; void *data)
{
git_http_auth_scheme *scheme = NULL;
git_http_auth_context *context = NULL, *c;
size_t i;
error = 0; *out = NULL;
on_error: for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
if (raw.size) if (scheme_match(&auth_schemes[i], data)) {
memset(raw.ptr, 0x0, raw.size); scheme = &auth_schemes[i];
break;
}
}
if (!scheme)
return 0;
/* 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;
}
}
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;
}
*out = context;
return 0;
}
static int apply_credentials(git_buf *buf, http_subtransport *t)
{
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;
git_buf_free(&raw); cred = t->url_cred;
return error; }
if (!cred)
return 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;
return context->next_token(buf, context, cred);
} }
static int gen_request( static int gen_request(
...@@ -137,18 +204,8 @@ static int gen_request( ...@@ -137,18 +204,8 @@ static int gen_request(
git_buf_puts(buf, "Accept: */*\r\n"); git_buf_puts(buf, "Accept: */*\r\n");
/* Apply credentials to the request */ /* Apply credentials to the request */
if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT && if (apply_credentials(buf, t) < 0)
t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
apply_basic_credential(buf, 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; return -1;
if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
}
git_buf_puts(buf, "\r\n"); git_buf_puts(buf, "\r\n");
...@@ -158,20 +215,26 @@ static int gen_request( ...@@ -158,20 +215,26 @@ static int gen_request(
return 0; return 0;
} }
static int parse_unauthorized_response( static int parse_authenticate_response(
git_vector *www_authenticate, git_vector *www_authenticate,
int *allowed_types, http_subtransport *t,
http_authmechanism_t *auth_mechanism) int *allowed_types)
{ {
unsigned i; git_http_auth_context *context;
char *entry; char *challenge;
size_t i;
git_vector_foreach(www_authenticate, i, entry) { git_vector_foreach(www_authenticate, i, challenge) {
if (!strncmp(entry, basic_authtype, 5) && if (auth_context_match(&context, t, challenge_match, challenge) < 0)
(entry[5] == '\0' || entry[5] == ' ')) { return -1;
*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; else if (!context)
*auth_mechanism = GIT_HTTP_AUTH_BASIC; continue;
}
if (context->set_challenge &&
context->set_challenge(context, challenge) < 0)
return -1;
*allowed_types |= context->credtypes;
} }
return 0; return 0;
...@@ -248,7 +311,7 @@ static int on_headers_complete(http_parser *parser) ...@@ -248,7 +311,7 @@ static int on_headers_complete(http_parser *parser)
http_subtransport *t = ctx->t; http_subtransport *t = ctx->t;
http_stream *s = ctx->s; http_stream *s = ctx->s;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
int error = 0, no_callback = 0; int error = 0, no_callback = 0, allowed_auth_types = 0;
/* Both parse_header_name and parse_header_value are populated /* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */ * and ready for consumption. */
...@@ -256,26 +319,26 @@ static int on_headers_complete(http_parser *parser) ...@@ -256,26 +319,26 @@ static int on_headers_complete(http_parser *parser)
if (on_header_ready(t) < 0) if (on_header_ready(t) < 0)
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
/* Check for an authentication failure. */ /* Capture authentication headers which may be a 401 (authentication
* is not complete) or a 200 (simply informing us that auth *is*
* complete.)
*/
if (parse_authenticate_response(&t->www_authenticate, t,
&allowed_auth_types) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
if (parser->status_code == 401 && /* Check for an authentication failure. */
get_verb == s->verb) { if (parser->status_code == 401 && get_verb == s->verb) {
if (!t->owner->cred_acquire_cb) { if (!t->owner->cred_acquire_cb) {
no_callback = 1; no_callback = 1;
} else { } else {
int allowed_types = 0; if (allowed_auth_types &&
(!t->cred || 0 == (t->cred->credtype & allowed_auth_types))) {
if (parse_unauthorized_response(&t->www_authenticate,
&allowed_types, &t->auth_mechanism) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
if (allowed_types &&
(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
error = t->owner->cred_acquire_cb(&t->cred, error = t->owner->cred_acquire_cb(&t->cred,
t->owner->url, t->owner->url,
t->connection_data.user, t->connection_data.user,
allowed_types, allowed_auth_types,
t->owner->cred_acquire_payload); t->owner->cred_acquire_payload);
if (error == GIT_PASSTHROUGH) { if (error == GIT_PASSTHROUGH) {
...@@ -286,7 +349,8 @@ static int on_headers_complete(http_parser *parser) ...@@ -286,7 +349,8 @@ static int on_headers_complete(http_parser *parser)
assert(t->cred); assert(t->cred);
/* Successfully acquired a credential. */ /* Successfully acquired a credential. */
return t->parse_error = PARSE_ERROR_REPLAY; t->parse_error = PARSE_ERROR_REPLAY;
return 0;
} }
} }
} }
...@@ -324,7 +388,8 @@ static int on_headers_complete(http_parser *parser) ...@@ -324,7 +388,8 @@ static int on_headers_complete(http_parser *parser)
t->connected = 0; t->connected = 0;
s->redirect_count++; s->redirect_count++;
return t->parse_error = PARSE_ERROR_REPLAY; t->parse_error = PARSE_ERROR_REPLAY;
return 0;
} }
/* Check for a 200 HTTP status code. */ /* Check for a 200 HTTP status code. */
...@@ -382,6 +447,13 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) ...@@ -382,6 +447,13 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
parser_context *ctx = (parser_context *) parser->data; parser_context *ctx = (parser_context *) parser->data;
http_subtransport *t = ctx->t; http_subtransport *t = ctx->t;
/* If our goal is to replay the request (either an auth failure or
* a redirect) then don't bother buffering since we're ignoring the
* content anyway.
*/
if (t->parse_error == PARSE_ERROR_REPLAY)
return 0;
if (ctx->buf_size < len) { if (ctx->buf_size < len) {
giterr_set(GITERR_NET, "Can't fit data in the buffer"); giterr_set(GITERR_NET, "Can't fit data in the buffer");
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
...@@ -456,7 +528,7 @@ static int http_connect(http_subtransport *t) ...@@ -456,7 +528,7 @@ static int http_connect(http_subtransport *t)
if (t->connected && if (t->connected &&
http_should_keep_alive(&t->parser) && http_should_keep_alive(&t->parser) &&
http_body_is_final(&t->parser)) t->parse_finished)
return 0; return 0;
if (t->socket.socket) if (t->socket.socket)
...@@ -502,10 +574,8 @@ replay: ...@@ -502,10 +574,8 @@ replay:
clear_parser_state(t); clear_parser_state(t);
if (gen_request(&request, s, 0) < 0) { if (gen_request(&request, s, 0) < 0)
giterr_set(GITERR_NET, "Failed to generate request");
return -1; return -1;
}
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request); git_buf_free(&request);
...@@ -604,10 +674,8 @@ static int http_stream_write_chunked( ...@@ -604,10 +674,8 @@ static int http_stream_write_chunked(
clear_parser_state(t); clear_parser_state(t);
if (gen_request(&request, s, 0) < 0) { if (gen_request(&request, s, 0) < 0)
giterr_set(GITERR_NET, "Failed to generate request");
return -1; return -1;
}
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request); git_buf_free(&request);
...@@ -679,10 +747,8 @@ static int http_stream_write_single( ...@@ -679,10 +747,8 @@ static int http_stream_write_single(
clear_parser_state(t); clear_parser_state(t);
if (gen_request(&request, s, len) < 0) { if (gen_request(&request, s, len) < 0)
giterr_set(GITERR_NET, "Failed to generate request");
return -1; return -1;
}
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
goto on_error; goto on_error;
...@@ -849,6 +915,8 @@ static int http_action( ...@@ -849,6 +915,8 @@ static int http_action(
static int http_close(git_smart_subtransport *subtransport) static int http_close(git_smart_subtransport *subtransport)
{ {
http_subtransport *t = (http_subtransport *) subtransport; http_subtransport *t = (http_subtransport *) subtransport;
git_http_auth_context *context;
size_t i;
clear_parser_state(t); clear_parser_state(t);
...@@ -867,6 +935,13 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -867,6 +935,13 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL; t->url_cred = NULL;
} }
git_vector_foreach(&t->auth_contexts, i, context) {
if (context->free)
context->free(context);
}
git_vector_clear(&t->auth_contexts);
gitno_connection_data_free_ptrs(&t->connection_data); gitno_connection_data_free_ptrs(&t->connection_data);
return 0; return 0;
...@@ -878,6 +953,7 @@ static void http_free(git_smart_subtransport *subtransport) ...@@ -878,6 +953,7 @@ static void http_free(git_smart_subtransport *subtransport)
http_close(subtransport); http_close(subtransport);
git_vector_free(&t->auth_contexts);
git__free(t); git__free(t);
} }
......
...@@ -101,7 +101,7 @@ static int apply_basic_credential(HINTERNET request, git_cred *cred) ...@@ -101,7 +101,7 @@ static int apply_basic_credential(HINTERNET request, git_cred *cred)
if (git_buf_oom(&raw) || if (git_buf_oom(&raw) ||
git_buf_puts(&buf, "Authorization: Basic ") < 0 || 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; goto on_error;
if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) { if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
......
...@@ -748,7 +748,7 @@ void test_core_buffer__unescape(void) ...@@ -748,7 +748,7 @@ void test_core_buffer__unescape(void)
assert_unescape("", ""); assert_unescape("", "");
} }
void test_core_buffer__base64(void) void test_core_buffer__encode_base64(void)
{ {
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
...@@ -759,33 +759,54 @@ void test_core_buffer__base64(void) ...@@ -759,33 +759,54 @@ void test_core_buffer__base64(void)
* 0x 1d 06 21 29 1c 30 * 0x 1d 06 21 29 1c 30
* d G h p c w * 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); cl_assert_equal_s("dGhpcw==", buf.ptr);
git_buf_clear(&buf); 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); cl_assert_equal_s("dGhpcyE=", buf.ptr);
git_buf_clear(&buf); 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); cl_assert_equal_s("dGhpcyEK", buf.ptr);
git_buf_free(&buf); git_buf_free(&buf);
} }
void test_core_buffer__base85(void) void test_core_buffer__decode_base64(void)
{ {
git_buf buf = GIT_BUF_INIT; 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); cl_assert_equal_s("bZBXF", buf.ptr);
git_buf_clear(&buf); 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); cl_assert_equal_s("ba!tca&BaE", buf.ptr);
git_buf_clear(&buf); 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"))); strlen("this is base 85 encoded")));
cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr); cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr);
git_buf_clear(&buf); git_buf_clear(&buf);
......
...@@ -226,9 +226,28 @@ void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void) ...@@ -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); 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) 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"); const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
git_cred_userpass_payload user_pass = { git_cred_userpass_payload user_pass = {
cl_getenv("GITTEST_REMOTE_USER"), cl_getenv("GITTEST_REMOTE_USER"),
...@@ -237,8 +256,12 @@ void test_online_clone__credentials(void) ...@@ -237,8 +256,12 @@ void test_online_clone__credentials(void)
if (!remote_url) return; if (!remote_url) return;
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.credentials = git_cred_userpass;
g_options.remote_callbacks.payload = &user_pass; g_options.remote_callbacks.payload = &user_pass;
}
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL; git_repository_free(g_repo); g_repo = NULL;
...@@ -346,7 +369,7 @@ void test_online_clone__ssh_with_paths(void) ...@@ -346,7 +369,7 @@ void test_online_clone__ssh_with_paths(void)
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); 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(); clar__skip();
g_options.remote_cb = custom_remote_ssh_with_paths; 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 ...@@ -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_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref)))); 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); cl_assert_equal_i(error, GIT_ITEROVER);
...@@ -852,6 +854,9 @@ void test_online_push__notes(void) ...@@ -852,6 +854,9 @@ void test_online_push__notes(void)
const char *specs[] = { "refs/notes/commits:refs/notes/commits" }; const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
push_status exp_stats[] = { { "refs/notes/commits", 1 } }; push_status exp_stats[] = { { "refs/notes/commits", 1 } };
expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } }; 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"); git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
target_oid = &_oid_b6; target_oid = &_oid_b6;
...@@ -864,5 +869,11 @@ void test_online_push__notes(void) ...@@ -864,5 +869,11 @@ void test_online_push__notes(void)
exp_stats, ARRAY_SIZE(exp_stats), exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); 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); 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