Unverified Commit 9bfad74d by Edward Thomson Committed by GitHub

Merge pull request #6533 from libgit2/ethomson/schannel-2

Introduce Schannel and SSPI for Windows
parents c058aa87 f15c8ac7
......@@ -130,19 +130,19 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
setup-script: osx
- name: "Windows (amd64, Visual Studio)"
- name: "Windows (amd64, Visual Studio, Schannel)"
id: windows-amd64-vs
os: windows-2019
setup-script: win32
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
BUILD_TEMP: D:\Temp
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, Visual Studio)"
- name: "Windows (x86, Visual Studio, WinHTTP)"
id: windows-x86-vs
os: windows-2019
setup-script: win32
......@@ -154,7 +154,7 @@ jobs:
BUILD_TEMP: D:\Temp
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (amd64, mingw)"
- name: "Windows (amd64, mingw, WinHTTP)"
id: windows-amd64-mingw
os: windows-2019
setup-script: mingw
......@@ -166,14 +166,14 @@ jobs:
BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, mingw)"
- name: "Windows (x86, mingw, Schannel)"
id: windows-x86-mingw
os: windows-2019
setup-script: mingw
env:
ARCH: x86
CMAKE_GENERATOR: MinGW Makefiles
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
BUILD_TEMP: D:\Temp
BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
......
......@@ -162,32 +162,39 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
setup-script: osx
- name: "Windows (amd64, Visual Studio)"
- name: "Windows (amd64, Visual Studio, WinHTTP)"
os: windows-2019
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON
CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=WinHTTP
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (no mmap)"
- name: "Windows (x86, Visual Studio, WinHTTP)"
os: windows-2019
env:
ARCH: x86
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=WinHTTP -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (amd64, Visual Studio, Schannel)"
os: windows-2019
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
CFLAGS: -DNO_MMAP
CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON
CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, Visual Studio)"
- name: "Windows (x86, Visual Studio, Schannel)"
os: windows-2019
env:
ARCH: x86
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel -DUSE_BUNDLED_ZLIB=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (amd64, mingw)"
- name: "Windows (amd64, mingw, WinHTTP)"
os: windows-2019
setup-script: mingw
env:
......@@ -198,17 +205,26 @@ jobs:
BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, mingw)"
- name: "Windows (x86, mingw, Schannel)"
os: windows-2019
setup-script: mingw
env:
ARCH: x86
CMAKE_GENERATOR: MinGW Makefiles
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
BUILD_TEMP: D:\Temp
BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (no mmap)"
os: windows-2019
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
CFLAGS: -DNO_MMAP
CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Linux (Bionic, GCC, dynamically-loaded OpenSSL)"
container:
name: bionic
......
......@@ -82,12 +82,6 @@ if(MSVC)
option(WIN32_LEAKCHECK "Enable leak reporting via crtdbg" OFF)
endif()
if(WIN32)
# By default, libgit2 is built with WinHTTP. To use the built-in
# HTTP transport, invoke CMake with the "-DUSE_WINHTTP=OFF" argument.
option(USE_WINHTTP "Use Win32 WinHTTP routines" ON)
endif()
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
......
......@@ -29,7 +29,7 @@ if(USE_GSSAPI)
list(APPEND LIBGIT2_SYSTEM_LIBS ${GSSFRAMEWORK_LIBRARIES})
set(GIT_GSSFRAMEWORK 1)
add_feature_info(SPNEGO GIT_GSSFRAMEWORK "SPNEGO authentication support (${USE_GSSAPI})")
add_feature_info(GSSAPI GIT_GSSFRAMEWORK "GSSAPI support for SPNEGO authentication (${USE_GSSAPI})")
elseif(USE_GSSAPI STREQUAL "gssapi")
if(NOT GSSAPI_FOUND)
message(FATAL_ERROR "Asked for gssapi GSS backend, but it wasn't found")
......@@ -38,11 +38,11 @@ if(USE_GSSAPI)
list(APPEND LIBGIT2_SYSTEM_LIBS ${GSSAPI_LIBRARIES})
set(GIT_GSSAPI 1)
add_feature_info(SPNEGO GIT_GSSAPI "SPNEGO authentication support (${USE_GSSAPI})")
add_feature_info(GSSAPI GIT_GSSAPI "GSSAPI support for SPNEGO authentication (${USE_GSSAPI})")
else()
message(FATAL_ERROR "Asked for backend ${USE_GSSAPI} but it wasn't found")
endif()
else()
set(GIT_GSSAPI 0)
add_feature_info(SPNEGO NO "SPNEGO authentication support")
add_feature_info(GSSAPI NO "GSSAPI support for SPNEGO authentication")
endif()
......@@ -19,7 +19,7 @@ if(USE_HTTPS)
message(STATUS "Security framework is too old, falling back to OpenSSL")
set(USE_HTTPS "OpenSSL")
endif()
elseif(USE_WINHTTP)
elseif(WIN32)
set(USE_HTTPS "WinHTTP")
elseif(OPENSSL_FOUND)
set(USE_HTTPS "OpenSSL")
......@@ -106,8 +106,27 @@ if(USE_HTTPS)
# https://github.com/ARMmbed/mbedtls/issues/228
# For now, pass its link flags as our own
list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
elseif(USE_HTTPS STREQUAL "Schannel")
set(GIT_SCHANNEL 1)
list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32" "secur32")
list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32" "-lsecur32")
elseif(USE_HTTPS STREQUAL "WinHTTP")
# WinHTTP setup was handled in the WinHTTP-specific block above
set(GIT_WINHTTP 1)
# Since MinGW does not come with headers or an import library for winhttp,
# we have to include a private header and generate our own import library
if(MINGW)
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/winhttp" "${PROJECT_BINARY_DIR}/deps/winhttp")
list(APPEND LIBGIT2_SYSTEM_LIBS winhttp)
list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/winhttp")
else()
list(APPEND LIBGIT2_SYSTEM_LIBS "winhttp")
list(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
endif()
list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32" "secur32")
list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32" "-lsecur32")
elseif(USE_HTTPS STREQUAL "OpenSSL-Dynamic")
set(GIT_OPENSSL 1)
set(GIT_OPENSSL_DYNAMIC 1)
......
......@@ -13,6 +13,8 @@ if(USE_SHA1 STREQUAL ON)
elseif(USE_SHA1 STREQUAL "HTTPS")
if(USE_HTTPS STREQUAL "SecureTransport")
set(USE_SHA1 "CommonCrypto")
elseif(USE_HTTPS STREQUAL "Schannel")
set(USE_SHA1 "Win32")
elseif(USE_HTTPS STREQUAL "WinHTTP")
set(USE_SHA1 "Win32")
elseif(USE_HTTPS)
......@@ -51,6 +53,8 @@ endif()
if(USE_SHA256 STREQUAL "HTTPS")
if(USE_HTTPS STREQUAL "SecureTransport")
set(USE_SHA256 "CommonCrypto")
elseif(USE_HTTPS STREQUAL "Schannel")
set(USE_SHA256 "Win32")
elseif(USE_HTTPS STREQUAL "WinHTTP")
set(USE_SHA256 "Win32")
elseif(USE_HTTPS)
......
if(WIN32 AND USE_WINHTTP)
set(GIT_WINHTTP 1)
# Since MinGW does not come with headers or an import library for winhttp,
# we have to include a private header and generate our own import library
if(MINGW)
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/winhttp" "${PROJECT_BINARY_DIR}/deps/winhttp")
list(APPEND LIBGIT2_SYSTEM_LIBS winhttp)
list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/winhttp")
else()
list(APPEND LIBGIT2_SYSTEM_LIBS "winhttp")
list(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
endif()
list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32")
list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32")
endif()
......@@ -42,7 +42,6 @@ include(SelectHashes)
include(SelectHTTPParser)
include(SelectRegex)
include(SelectSSH)
include(SelectWinHTTP)
include(SelectZlib)
#
......
......@@ -30,6 +30,7 @@
#include "streams/registry.h"
#include "streams/mbedtls.h"
#include "streams/openssl.h"
#include "streams/socket.h"
#include "transports/smart.h"
#include "transports/http.h"
#include "transports/ssh.h"
......@@ -78,6 +79,7 @@ int git_libgit2_init(void)
git_merge_driver_global_init,
git_transport_ssh_global_init,
git_stream_registry_global_init,
git_socket_stream_global_init,
git_openssl_stream_global_init,
git_mbedtls_stream_global_init,
git_mwindow_global_init,
......
/*
* 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_steams_schannel_h__
#define INCLUDE_steams_schannel_h__
#include "common.h"
#include "git2/sys/stream.h"
#ifdef GIT_SCHANNEL
extern int git_schannel_stream_new(
git_stream **out,
const char *host,
const char *port);
extern int git_schannel_stream_wrap(
git_stream **out,
git_stream *in,
const char *host);
#endif
#endif
......@@ -10,22 +10,23 @@
#include "posix.h"
#include "netops.h"
#include "registry.h"
#include "runtime.h"
#include "stream.h"
#ifndef _WIN32
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/select.h>
# include <sys/time.h>
# include <netdb.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/select.h>
# include <sys/time.h>
# include <netdb.h>
# include <netinet/in.h>
# include <arpa/inet.h>
#else
# include <winsock2.h>
# include <ws2tcpip.h>
# ifdef _MSC_VER
# pragma comment(lib, "ws2_32")
# endif
# include <winsock2.h>
# include <ws2tcpip.h>
# ifdef _MSC_VER
# pragma comment(lib, "ws2_32")
# endif
#endif
#ifdef GIT_WIN32
......@@ -54,11 +55,8 @@ static int close_socket(GIT_SOCKET s)
return 0;
#ifdef GIT_WIN32
if (SOCKET_ERROR == closesocket(s))
return -1;
if (0 != WSACleanup()) {
git_error_set(GIT_ERROR_OS, "winsock cleanup failed");
if (closesocket(s) != 0) {
net_set_error("could not close socket");
return -1;
}
......@@ -77,23 +75,6 @@ static int socket_connect(git_stream *stream)
GIT_SOCKET s = INVALID_SOCKET;
int ret;
#ifdef GIT_WIN32
/* on win32, the WSA context needs to be initialized
* before any socket calls can be performed */
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
git_error_set(GIT_ERROR_OS, "winsock init failed");
return -1;
}
if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
WSACleanup();
git_error_set(GIT_ERROR_OS, "winsock init failed");
return -1;
}
#endif
memset(&hints, 0x0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
......@@ -240,3 +221,42 @@ int git_socket_stream_new(
return init(out, host, port);
}
#ifdef GIT_WIN32
static void socket_stream_global_shutdown(void)
{
WSACleanup();
}
int git_socket_stream_global_init(void)
{
WORD winsock_version;
WSADATA wsa_data;
winsock_version = MAKEWORD(2, 2);
if (WSAStartup(winsock_version, &wsa_data) != 0) {
git_error_set(GIT_ERROR_OS, "could not initialize Windows Socket Library");
return -1;
}
if (LOBYTE(wsa_data.wVersion) != 2 ||
HIBYTE(wsa_data.wVersion) != 2) {
git_error_set(GIT_ERROR_SSL, "Windows Socket Library does not support Winsock 2.2");
return -1;
}
return git_runtime_shutdown_register(socket_stream_global_shutdown);
}
#else
#include "stream.h"
int git_socket_stream_global_init(void)
{
return 0;
}
#endif
......@@ -20,4 +20,6 @@ typedef struct {
extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
extern int git_socket_stream_global_init(void);
#endif
......@@ -13,6 +13,7 @@
#include "streams/mbedtls.h"
#include "streams/openssl.h"
#include "streams/stransport.h"
#include "streams/schannel.h"
int git_tls_stream_new(git_stream **out, const char *host, const char *port)
{
......@@ -33,6 +34,8 @@ int git_tls_stream_new(git_stream **out, const char *host, const char *port)
init = git_openssl_stream_new;
#elif defined(GIT_MBEDTLS)
init = git_mbedtls_stream_new;
#elif defined(GIT_SCHANNEL)
init = git_schannel_stream_new;
#endif
} else {
return error;
......@@ -63,6 +66,8 @@ int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host)
wrap = git_openssl_stream_wrap;
#elif defined(GIT_MBEDTLS)
wrap = git_mbedtls_stream_wrap;
#elif defined(GIT_SCHANNEL)
wrap = git_schannel_stream_wrap;
#endif
}
......
......@@ -20,13 +20,13 @@
#include <krb5.h>
#endif
static gss_OID_desc negotiate_oid_spnego =
static gss_OID_desc gssapi_oid_spnego =
{ 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
static gss_OID_desc negotiate_oid_krb5 =
static gss_OID_desc gssapi_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 };
static gss_OID gssapi_oids[] =
{ &gssapi_oid_spnego, &gssapi_oid_krb5, NULL };
typedef struct {
git_http_auth_context parent;
......@@ -36,9 +36,9 @@ typedef struct {
char *challenge;
gss_ctx_id_t gss_context;
gss_OID oid;
} http_auth_negotiate_context;
} http_auth_gssapi_context;
static void negotiate_err_set(
static void gssapi_err_set(
OM_uint32 status_major,
OM_uint32 status_minor,
const char *message)
......@@ -58,11 +58,11 @@ static void negotiate_err_set(
}
}
static int negotiate_set_challenge(
static int gssapi_set_challenge(
git_http_auth_context *c,
const char *challenge)
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
GIT_ASSERT_ARG(ctx);
GIT_ASSERT_ARG(challenge);
......@@ -76,7 +76,7 @@ static int negotiate_set_challenge(
return 0;
}
static void negotiate_context_dispose(http_auth_negotiate_context *ctx)
static void gssapi_context_dispose(http_auth_gssapi_context *ctx)
{
OM_uint32 status_minor;
......@@ -92,12 +92,12 @@ static void negotiate_context_dispose(http_auth_negotiate_context *ctx)
ctx->challenge = NULL;
}
static int negotiate_next_token(
static int gssapi_next_token(
git_str *buf,
git_http_auth_context *c,
git_credential *cred)
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
OM_uint32 status_major, status_minor;
gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
input_token = GSS_C_EMPTY_BUFFER,
......@@ -126,7 +126,7 @@ static int negotiate_next_token(
GSS_C_NT_HOSTBASED_SERVICE, &server);
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor,
gssapi_err_set(status_major, status_minor,
"could not parse principal");
error = -1;
goto done;
......@@ -152,10 +152,10 @@ static int negotiate_next_token(
input_token.length = input_buf.size;
input_token_ptr = &input_token;
} else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
negotiate_context_dispose(ctx);
gssapi_context_dispose(ctx);
}
mech = &negotiate_oid_spnego;
mech = &gssapi_oid_spnego;
status_major = gss_init_sec_context(
&status_minor,
......@@ -173,14 +173,14 @@ static int negotiate_next_token(
NULL);
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor, "negotiate failure");
gssapi_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) {
negotiate_context_dispose(ctx);
gssapi_context_dispose(ctx);
ctx->complete = 1;
goto done;
}
......@@ -204,20 +204,20 @@ done:
return error;
}
static int negotiate_is_complete(git_http_auth_context *c)
static int gssapi_is_complete(git_http_auth_context *c)
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
GIT_ASSERT_ARG(ctx);
return (ctx->complete == 1);
}
static void negotiate_context_free(git_http_auth_context *c)
static void gssapi_context_free(git_http_auth_context *c)
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
negotiate_context_dispose(ctx);
gssapi_context_dispose(ctx);
ctx->configured = 0;
ctx->complete = 0;
......@@ -226,8 +226,8 @@ static void negotiate_context_free(git_http_auth_context *c)
git__free(ctx);
}
static int negotiate_init_context(
http_auth_negotiate_context *ctx,
static int gssapi_init_context(
http_auth_gssapi_context *ctx,
const git_net_url *url)
{
OM_uint32 status_major, status_minor;
......@@ -239,13 +239,13 @@ static int negotiate_init_context(
status_major = gss_indicate_mechs(&status_minor, &mechanism_list);
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor,
gssapi_err_set(status_major, status_minor,
"could not query mechanisms");
return -1;
}
if (mechanism_list) {
for (oid = negotiate_oids; *oid; oid++) {
for (oid = gssapi_oids; *oid; oid++) {
for (i = 0; i < mechanism_list->count; i++) {
item = &mechanism_list->elements[i];
......@@ -285,14 +285,14 @@ int git_http_auth_negotiate(
git_http_auth_context **out,
const git_net_url *url)
{
http_auth_negotiate_context *ctx;
http_auth_gssapi_context *ctx;
*out = NULL;
ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
ctx = git__calloc(1, sizeof(http_auth_gssapi_context));
GIT_ERROR_CHECK_ALLOC(ctx);
if (negotiate_init_context(ctx, url) < 0) {
if (gssapi_init_context(ctx, url) < 0) {
git__free(ctx);
return -1;
}
......@@ -300,10 +300,10 @@ int git_http_auth_negotiate(
ctx->parent.type = GIT_HTTP_AUTH_NEGOTIATE;
ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
ctx->parent.connection_affinity = 1;
ctx->parent.set_challenge = negotiate_set_challenge;
ctx->parent.next_token = negotiate_next_token;
ctx->parent.is_complete = negotiate_is_complete;
ctx->parent.free = negotiate_context_free;
ctx->parent.set_challenge = gssapi_set_challenge;
ctx->parent.next_token = gssapi_next_token;
ctx->parent.is_complete = gssapi_is_complete;
ctx->parent.free = gssapi_context_free;
*out = (git_http_auth_context *)ctx;
......
......@@ -12,7 +12,7 @@
#include "git2.h"
#include "auth.h"
#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK)
#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK) || defined(GIT_WIN32)
extern int git_http_auth_negotiate(
git_http_auth_context **out,
......
......@@ -13,7 +13,7 @@
/* NTLM requires a full request/challenge/response */
#define GIT_AUTH_STEPS_NTLM 2
#ifdef GIT_NTLM
#if defined(GIT_NTLM) || defined(GIT_WIN32)
#if defined(GIT_OPENSSL)
# define CRYPT_OPENSSL
......
......@@ -23,7 +23,7 @@ typedef struct {
bool complete;
} http_auth_ntlm_context;
static int ntlm_set_challenge(
static int ntlmclient_set_challenge(
git_http_auth_context *c,
const char *challenge)
{
......@@ -40,7 +40,7 @@ static int ntlm_set_challenge(
return 0;
}
static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred)
static int ntlmclient_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred)
{
git_credential_userpass_plaintext *cred;
const char *sep, *username;
......@@ -76,7 +76,7 @@ done:
return error;
}
static int ntlm_next_token(
static int ntlmclient_next_token(
git_str *buf,
git_http_auth_context *c,
git_credential *cred)
......@@ -104,7 +104,7 @@ static int ntlm_next_token(
*/
ctx->complete = true;
if (cred && ntlm_set_credentials(ctx, cred) != 0)
if (cred && ntlmclient_set_credentials(ctx, cred) != 0)
goto done;
if (challenge_len < 4) {
......@@ -162,7 +162,7 @@ done:
return error;
}
static int ntlm_is_complete(git_http_auth_context *c)
static int ntlmclient_is_complete(git_http_auth_context *c)
{
http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
......@@ -170,7 +170,7 @@ static int ntlm_is_complete(git_http_auth_context *c)
return (ctx->complete == true);
}
static void ntlm_context_free(git_http_auth_context *c)
static void ntlmclient_context_free(git_http_auth_context *c)
{
http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
......@@ -179,7 +179,7 @@ static void ntlm_context_free(git_http_auth_context *c)
git__free(ctx);
}
static int ntlm_init_context(
static int ntlmclient_init_context(
http_auth_ntlm_context *ctx,
const git_net_url *url)
{
......@@ -206,7 +206,7 @@ int git_http_auth_ntlm(
ctx = git__calloc(1, sizeof(http_auth_ntlm_context));
GIT_ERROR_CHECK_ALLOC(ctx);
if (ntlm_init_context(ctx, url) < 0) {
if (ntlmclient_init_context(ctx, url) < 0) {
git__free(ctx);
return -1;
}
......@@ -214,10 +214,10 @@ int git_http_auth_ntlm(
ctx->parent.type = GIT_HTTP_AUTH_NTLM;
ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT;
ctx->parent.connection_affinity = 1;
ctx->parent.set_challenge = ntlm_set_challenge;
ctx->parent.next_token = ntlm_next_token;
ctx->parent.is_complete = ntlm_is_complete;
ctx->parent.free = ntlm_context_free;
ctx->parent.set_challenge = ntlmclient_set_challenge;
ctx->parent.next_token = ntlmclient_next_token;
ctx->parent.is_complete = ntlmclient_is_complete;
ctx->parent.free = ntlmclient_context_free;
*out = (git_http_auth_context *)ctx;
......
/*
* 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 "auth_ntlm.h"
#include "auth_negotiate.h"
#ifdef GIT_WIN32
#define SECURITY_WIN32
#include "git2.h"
#include "auth.h"
#include "git2/sys/credential.h"
#include <windows.h>
#include <security.h>
typedef struct {
git_http_auth_context parent;
wchar_t *target;
const char *package_name;
size_t package_name_len;
wchar_t *package_name_w;
SecPkgInfoW *package_info;
SEC_WINNT_AUTH_IDENTITY_W identity;
CredHandle cred;
CtxtHandle context;
int has_identity : 1,
has_credentials : 1,
has_context : 1,
complete : 1;
git_str challenge;
} http_auth_sspi_context;
static void sspi_reset_context(http_auth_sspi_context *ctx)
{
if (ctx->has_identity) {
git__free(ctx->identity.User);
git__free(ctx->identity.Domain);
git__free(ctx->identity.Password);
memset(&ctx->identity, 0, sizeof(SEC_WINNT_AUTH_IDENTITY_W));
ctx->has_identity = 0;
}
if (ctx->has_credentials) {
FreeCredentialsHandle(&ctx->cred);
memset(&ctx->cred, 0, sizeof(CredHandle));
ctx->has_credentials = 0;
}
if (ctx->has_context) {
DeleteSecurityContext(&ctx->context);
memset(&ctx->context, 0, sizeof(CtxtHandle));
ctx->has_context = 0;
}
ctx->complete = 0;
git_str_dispose(&ctx->challenge);
}
static int sspi_set_challenge(
git_http_auth_context *c,
const char *challenge)
{
http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
size_t challenge_len = strlen(challenge);
git_str_clear(&ctx->challenge);
if (strncmp(challenge, ctx->package_name, ctx->package_name_len) != 0) {
git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
return -1;
}
/*
* A package type indicator without a base64 payload indicates the
* mechanism; it's not an actual challenge. Ignore it.
*/
if (challenge[ctx->package_name_len] == 0) {
return 0;
} else if (challenge[ctx->package_name_len] != ' ') {
git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
return -1;
}
if (git_str_decode_base64(&ctx->challenge,
challenge + (ctx->package_name_len + 1),
challenge_len - (ctx->package_name_len + 1)) < 0) {
git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
return -1;
}
GIT_ASSERT(ctx->challenge.size <= ULONG_MAX);
return 0;
}
static int create_identity(
SEC_WINNT_AUTH_IDENTITY_W **out,
http_auth_sspi_context *ctx,
git_credential *cred)
{
git_credential_userpass_plaintext *userpass;
wchar_t *username = NULL, *domain = NULL, *password = NULL;
int username_len = 0, domain_len = 0, password_len = 0;
const char *sep;
if (cred->credtype == GIT_CREDENTIAL_DEFAULT) {
*out = NULL;
return 0;
}
if (cred->credtype != GIT_CREDENTIAL_USERPASS_PLAINTEXT) {
git_error_set(GIT_ERROR_NET, "unknown credential type: %d", cred->credtype);
return -1;
}
userpass = (git_credential_userpass_plaintext *)cred;
if ((sep = strchr(userpass->username, '\\')) != NULL) {
GIT_ASSERT(sep - userpass->username < INT_MAX);
username_len = git_utf8_to_16_alloc(&username, sep + 1);
domain_len = git_utf8_to_16_alloc_with_len(&domain,
userpass->username, (int)(sep - userpass->username));
} else {
username_len = git_utf8_to_16_alloc(&username,
userpass->username);
}
password_len = git_utf8_to_16_alloc(&password, userpass->password);
if (username_len < 0 || domain_len < 0 || password_len < 0) {
git__free(username);
git__free(domain);
git__free(password);
return -1;
}
ctx->identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
ctx->identity.User = username;
ctx->identity.UserLength = (unsigned long)username_len;
ctx->identity.Password = password;
ctx->identity.PasswordLength = (unsigned long)password_len;
ctx->identity.Domain = domain;
ctx->identity.DomainLength = (unsigned long)domain_len;
ctx->has_identity = 1;
*out = &ctx->identity;
return 0;
}
static int sspi_next_token(
git_str *buf,
git_http_auth_context *c,
git_credential *cred)
{
http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
SEC_WINNT_AUTH_IDENTITY_W *identity = NULL;
TimeStamp timestamp;
DWORD context_flags;
SecBuffer input_buf = { 0, SECBUFFER_TOKEN, NULL };
SecBuffer output_buf = { 0, SECBUFFER_TOKEN, NULL };
SecBufferDesc input_buf_desc = { SECBUFFER_VERSION, 1, &input_buf };
SecBufferDesc output_buf_desc = { SECBUFFER_VERSION, 1, &output_buf };
SECURITY_STATUS status;
if (ctx->complete)
sspi_reset_context(ctx);
if (!ctx->has_context) {
if (create_identity(&identity, ctx, cred) < 0)
return -1;
status = AcquireCredentialsHandleW(NULL, ctx->package_name_w,
SECPKG_CRED_BOTH, NULL, identity, NULL,
NULL, &ctx->cred, &timestamp);
if (status != SEC_E_OK) {
git_error_set(GIT_ERROR_OS, "could not acquire credentials");
return -1;
}
ctx->has_credentials = 1;
}
context_flags = ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_CONFIDENTIALITY |
ISC_REQ_MUTUAL_AUTH;
if (ctx->challenge.size > 0) {
input_buf.BufferType = SECBUFFER_TOKEN;
input_buf.cbBuffer = (unsigned long)ctx->challenge.size;
input_buf.pvBuffer = ctx->challenge.ptr;
}
status = InitializeSecurityContextW(&ctx->cred,
ctx->has_context ? &ctx->context : NULL,
ctx->target,
context_flags,
0,
SECURITY_NETWORK_DREP,
ctx->has_context ? &input_buf_desc : NULL,
0,
ctx->has_context ? NULL : &ctx->context,
&output_buf_desc,
&context_flags,
NULL);
if (status == SEC_I_COMPLETE_AND_CONTINUE ||
status == SEC_I_COMPLETE_NEEDED)
status = CompleteAuthToken(&ctx->context, &output_buf_desc);
if (status == SEC_E_OK) {
ctx->complete = 1;
} else if (status != SEC_I_CONTINUE_NEEDED) {
git_error_set(GIT_ERROR_OS, "could not initialize security context");
return -1;
}
ctx->has_context = 1;
git_str_clear(&ctx->challenge);
if (output_buf.cbBuffer > 0) {
git_str_put(buf, ctx->package_name, ctx->package_name_len);
git_str_putc(buf, ' ');
git_str_encode_base64(buf, output_buf.pvBuffer, output_buf.cbBuffer);
FreeContextBuffer(output_buf.pvBuffer);
if (git_str_oom(buf))
return -1;
}
return 0;
}
static int sspi_is_complete(git_http_auth_context *c)
{
http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
return ctx->complete;
}
static void sspi_context_free(git_http_auth_context *c)
{
http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
sspi_reset_context(ctx);
FreeContextBuffer(ctx->package_info);
git__free(ctx->target);
git__free(ctx);
}
static int sspi_init_context(
git_http_auth_context **out,
git_http_auth_t type,
const git_net_url *url)
{
http_auth_sspi_context *ctx;
git_str target = GIT_STR_INIT;
*out = NULL;
ctx = git__calloc(1, sizeof(http_auth_sspi_context));
GIT_ERROR_CHECK_ALLOC(ctx);
switch (type) {
case GIT_HTTP_AUTH_NTLM:
ctx->package_name = "NTLM";
ctx->package_name_len = CONST_STRLEN("NTLM");
ctx->package_name_w = L"NTLM";
ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT |
GIT_CREDENTIAL_DEFAULT;
break;
case GIT_HTTP_AUTH_NEGOTIATE:
ctx->package_name = "Negotiate";
ctx->package_name_len = CONST_STRLEN("Negotiate");
ctx->package_name_w = L"Negotiate";
ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
break;
default:
git_error_set(GIT_ERROR_NET, "unknown SSPI auth type: %d", ctx->parent.type);
git__free(ctx);
return -1;
}
if (QuerySecurityPackageInfoW(ctx->package_name_w, &ctx->package_info) != SEC_E_OK) {
git_error_set(GIT_ERROR_OS, "could not query security package");
git__free(ctx);
return -1;
}
if (git_str_printf(&target, "http/%s", url->host) < 0 ||
git_utf8_to_16_alloc(&ctx->target, target.ptr) < 0) {
FreeContextBuffer(ctx->package_info);
git__free(ctx);
return -1;
}
ctx->parent.type = type;
ctx->parent.connection_affinity = 1;
ctx->parent.set_challenge = sspi_set_challenge;
ctx->parent.next_token = sspi_next_token;
ctx->parent.is_complete = sspi_is_complete;
ctx->parent.free = sspi_context_free;
*out = (git_http_auth_context *)ctx;
git_str_dispose(&target);
return 0;
}
int git_http_auth_negotiate(
git_http_auth_context **out,
const git_net_url *url)
{
return sspi_init_context(out, GIT_HTTP_AUTH_NEGOTIATE, url);
}
int git_http_auth_ntlm(
git_http_auth_context **out,
const git_net_url *url)
{
return sspi_init_context(out, GIT_HTTP_AUTH_NTLM, url);
}
#endif /* GIT_WIN32 */
......@@ -158,10 +158,10 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha
goto done;
}
if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0)
if ((error = user_len = git_utf8_to_16_alloc(&user, c->username)) < 0)
goto done;
if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0)
if ((error = pass_len = git_utf8_to_16_alloc(&pass, c->password)) < 0)
goto done;
if (!WinHttpSetCredentials(request, target, native_scheme, user, pass, NULL)) {
......@@ -242,7 +242,7 @@ static int acquire_fallback_cred(
HRESULT hCoInitResult;
/* Convert URL to wide characters */
if (git__utf8_to_16_alloc(&wide_url, url) < 0) {
if (git_utf8_to_16_alloc(&wide_url, url) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert string to wide form");
return -1;
}
......@@ -397,7 +397,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
return -1;
/* Convert URL to wide characters */
if (git__utf8_to_16_alloc(&s->request_uri, git_str_cstr(&buf)) < 0) {
if (git_utf8_to_16_alloc(&s->request_uri, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert string to wide form");
goto on_error;
}
......@@ -473,7 +473,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* Convert URL to wide characters */
error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr);
error = git_utf8_to_16_alloc(&proxy_wide, processed_url.ptr);
git_str_dispose(&processed_url);
if (error < 0)
goto on_error;
......@@ -531,7 +531,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
s->service) < 0)
goto on_error;
if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
if (git_utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert content-type to wide characters");
goto on_error;
}
......@@ -548,7 +548,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
s->service) < 0)
goto on_error;
if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
if (git_utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert accept header to wide characters");
goto on_error;
}
......@@ -568,7 +568,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
git_str_puts(&buf, t->owner->connect_opts.custom_headers.strings[i]);
/* Convert header to wide characters */
if ((error = git__utf8_to_16_alloc(&custom_header_wide, git_str_cstr(&buf))) < 0)
if ((error = git_utf8_to_16_alloc(&custom_header_wide, git_str_cstr(&buf))) < 0)
goto on_error;
if (!WinHttpAddRequestHeaders(s->request, custom_header_wide, (ULONG)-1L,
......@@ -783,7 +783,7 @@ static int winhttp_connect(
}
/* Prepare host */
if (git__utf8_to_16_alloc(&wide_host, host) < 0) {
if (git_utf8_to_16_alloc(&wide_host, host) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
goto on_error;
}
......@@ -792,7 +792,7 @@ static int winhttp_connect(
if (git_http__user_agent(&ua) < 0)
goto on_error;
if (git__utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
if (git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
goto on_error;
}
......@@ -1182,7 +1182,7 @@ replay:
}
/* Convert the Location header to UTF-8 */
if (git__utf16_to_8_alloc(&location8, location) < 0) {
if (git_utf8_from_16_alloc(&location8, location) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert Location header to UTF-8");
git__free(location);
return -1;
......@@ -1254,7 +1254,7 @@ replay:
else
p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
if (git_utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert expected content-type to wide characters");
return -1;
}
......
......@@ -2015,7 +2015,7 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
git_win32_path fullpath_w, executable_w;
int error;
if (git__utf8_to_16(executable_w, GIT_WIN_PATH_MAX, executable) < 0)
if (git_utf8_to_16(executable_w, GIT_WIN_PATH_MAX, executable) < 0)
return -1;
error = git_win32_path_find_executable(fullpath_w, executable_w);
......
......@@ -41,6 +41,7 @@
#cmakedefine GIT_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SECURE_TRANSPORT 1
#cmakedefine GIT_MBEDTLS 1
#cmakedefine GIT_SCHANNEL 1
#cmakedefine GIT_SHA1_COLLISIONDETECT 1
#cmakedefine GIT_SHA1_WIN32 1
......
......@@ -743,7 +743,7 @@ int git__getenv(git_str *out, const char *name)
git_str_clear(out);
if (git__utf8_to_16_alloc(&wide_name, name) < 0)
if (git_utf8_to_16_alloc(&wide_name, name) < 0)
return -1;
if ((value_len = GetEnvironmentVariableW(wide_name, NULL, 0)) > 0) {
......
......@@ -43,7 +43,7 @@ char *git_win32_get_error_message(DWORD error_code)
(LPWSTR)&lpMsgBuf, 0, NULL)) {
/* Convert the message to UTF-8. If this fails, we will
* return NULL, which is a condition expected by the caller */
if (git__utf16_to_8_alloc(&utf8_msg, lpMsgBuf) < 0)
if (git_utf8_from_16_alloc(&utf8_msg, lpMsgBuf) < 0)
utf8_msg = NULL;
LocalFree(lpMsgBuf);
......
......@@ -336,13 +336,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
/* See if this is an absolute path (beginning with a drive letter) */
if (git_fs_path_is_absolute(src)) {
if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0)
if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0)
goto on_error;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
goto on_error;
}
/* UNC paths */
......@@ -351,7 +351,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
dest += 4;
/* Skip the leading "\\" */
if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX - 2, src + 2) < 0)
if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX - 2, src + 2) < 0)
goto on_error;
}
/* Absolute paths omitting the drive letter */
......@@ -365,7 +365,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
}
/* Skip the drive letter specification ("C:") */
if (git__utf8_to_16(dest + 2, GIT_WIN_PATH_MAX - 2, src) < 0)
if (git_utf8_to_16(dest + 2, GIT_WIN_PATH_MAX - 2, src) < 0)
goto on_error;
}
/* Relative paths */
......@@ -377,7 +377,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
dest[cwd_len++] = L'\\';
if (git__utf8_to_16(dest + cwd_len, GIT_WIN_PATH_MAX - cwd_len, src) < 0)
if (git_utf8_to_16(dest + cwd_len, GIT_WIN_PATH_MAX - cwd_len, src) < 0)
goto on_error;
}
......@@ -404,7 +404,7 @@ int git_win32_path_relative_from_utf8(git_win32_path out, const char *src)
return git_win32_path_from_utf8(out, src);
}
if ((len = git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src)) < 0)
if ((len = git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src)) < 0)
return -1;
for (p = dest; p < (dest + len); p++) {
......@@ -433,7 +433,7 @@ int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
}
}
if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0)
if ((len = git_utf8_from_16(out, GIT_WIN_PATH_UTF8, src)) < 0)
return len;
git_fs_path_mkposix(dest);
......@@ -471,7 +471,7 @@ char *git_win32_path_8dot3_name(const char *path)
if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
return NULL;
if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0)
if ((len = git_utf8_from_16(shortname, namelen + 1, start)) < 0)
return NULL;
return shortname;
......
......@@ -649,7 +649,7 @@ int p_getcwd(char *buffer_out, size_t size)
git_win32_path_remove_namespace(cwd, wcslen(cwd));
/* Convert the working directory back to UTF-8 */
if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
if (git_utf8_from_16(buffer_out, size, cwd) < 0) {
DWORD code = GetLastError();
if (code == ERROR_INSUFFICIENT_BUFFER)
......
......@@ -15,108 +15,114 @@ GIT_INLINE(void) git__set_errno(void)
errno = EINVAL;
}
/**
* Converts a UTF-8 string to wide characters.
*
* @param dest The buffer to receive the wide string.
* @param dest_size The size of the buffer, in characters.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
*/
int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
int git_utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
{
/* Length of -1 indicates NULL termination of the input string. */
return git_utf8_to_16_with_len(dest, dest_size, src, -1);
}
int git_utf8_to_16_with_len(
wchar_t *dest,
size_t _dest_size,
const char *src,
int src_len)
{
int dest_size = (int)min(_dest_size, INT_MAX);
int len;
/* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
* turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
* length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */
if ((len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1) < 0)
/*
* Subtract 1 from the result to turn 0 into -1 (an error code) and
* to not count the NULL terminator as part of the string's length.
* MultiByteToWideChar never returns int's minvalue, so underflow
* is not possible.
*/
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
src, src_len, dest, dest_size) - 1;
if (len < 0)
git__set_errno();
return len;
}
/**
* Converts a wide string to UTF-8.
*
* @param dest The buffer to receive the UTF-8 string.
* @param dest_size The size of the buffer, in bytes.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
*/
int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
int git_utf8_from_16(char *dest, size_t dest_size, const wchar_t *src)
{
/* Length of -1 indicates NULL termination of the input string. */
return git_utf8_from_16_with_len(dest, dest_size, src, -1);
}
int git_utf8_from_16_with_len(
char *dest,
size_t _dest_size,
const wchar_t *src,
int src_len)
{
int dest_size = (int)min(_dest_size, INT_MAX);
int len;
/* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
* turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
* length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */
if ((len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0)
/*
* Subtract 1 from the result to turn 0 into -1 (an error code) and
* to not count the NULL terminator as part of the string's length.
* WideCharToMultiByte never returns int's minvalue, so underflow
* is not possible.
*/
len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
src, src_len, dest, dest_size, NULL, NULL) - 1;
if (len < 0)
git__set_errno();
return len;
}
/**
* Converts a UTF-8 string to wide characters.
* Memory is allocated to hold the converted string.
* The caller is responsible for freeing the string with git__free.
*
* @param dest Receives a pointer to the wide string.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
*/
int git__utf8_to_16_alloc(wchar_t **dest, const char *src)
int git_utf8_to_16_alloc(wchar_t **dest, const char *src)
{
/* Length of -1 indicates NULL termination of the input string. */
return git_utf8_to_16_alloc_with_len(dest, src, -1);
}
int git_utf8_to_16_alloc_with_len(wchar_t **dest, const char *src, int src_len)
{
int utf16_size;
*dest = NULL;
/* Length of -1 indicates NULL termination of the input string */
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
src, src_len, NULL, 0);
if (!utf16_size) {
git__set_errno();
return -1;
}
if (!(*dest = git__mallocarray(utf16_size, sizeof(wchar_t)))) {
errno = ENOMEM;
return -1;
}
*dest = git__mallocarray(utf16_size, sizeof(wchar_t));
GIT_ERROR_CHECK_ALLOC(*dest);
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size);
if (!utf16_size) {
git__set_errno();
utf16_size = git_utf8_to_16_with_len(*dest, (size_t)utf16_size,
src, src_len);
if (utf16_size < 0) {
git__free(*dest);
*dest = NULL;
}
/* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL
* terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue,
* so underflow is not possible */
return utf16_size - 1;
return utf16_size;
}
/**
* Converts a wide string to UTF-8.
* Memory is allocated to hold the converted string.
* The caller is responsible for freeing the string with git__free.
*
* @param dest Receives a pointer to the UTF-8 string.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
*/
int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
int git_utf8_from_16_alloc(char **dest, const wchar_t *src)
{
/* Length of -1 indicates NULL termination of the input string. */
return git_utf8_from_16_alloc_with_len(dest, src, -1);
}
int git_utf8_from_16_alloc_with_len(char **dest, const wchar_t *src, int src_len)
{
int utf8_size;
*dest = NULL;
/* Length of -1 indicates NULL termination of the input string */
utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);
utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
src, src_len, NULL, 0, NULL, NULL);
if (!utf8_size) {
git__set_errno();
......@@ -124,23 +130,15 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
}
*dest = git__malloc(utf8_size);
GIT_ERROR_CHECK_ALLOC(*dest);
if (!*dest) {
errno = ENOMEM;
return -1;
}
utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, *dest, utf8_size, NULL, NULL);
if (!utf8_size) {
git__set_errno();
utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
src, src_len, *dest, utf8_size, NULL, NULL);
if (utf8_size < 0) {
git__free(*dest);
*dest = NULL;
}
/* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL
* terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue,
* so underflow is not possible */
return utf8_size - 1;
return utf8_size;
}
......@@ -16,14 +16,45 @@
#endif
/**
* Converts a NUL-terminated UTF-8 string to wide characters. This is a
* convenience function for `git_utf8_to_16_with_len`.
*
* @param dest The buffer to receive the wide string.
* @param dest_size The size of the buffer, in characters.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters
* (not counting the NULL terminator), or < 0 for failure
*/
int git_utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
/**
* Converts a UTF-8 string to wide characters.
*
* @param dest The buffer to receive the wide string.
* @param dest_size The size of the buffer, in characters.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
* @param src_len The length of the string to convert.
* @return The length of the wide string, in characters
* (not counting the NULL terminator), or < 0 for failure
*/
int git_utf8_to_16_with_len(
wchar_t *dest,
size_t dest_size,
const char *src,
int src_len);
/**
* Converts a NUL-terminated wide string to UTF-8. This is a convenience
* function for `git_utf8_from_16_with_len`.
*
* @param dest The buffer to receive the UTF-8 string.
* @param dest_size The size of the buffer, in bytes.
* @param src The wide string to convert.
* @param src_len The length of the string to convert.
* @return The length of the UTF-8 string, in bytes
* (not counting the NULL terminator), or < 0 for failure
*/
int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
int git_utf8_from_16(char *dest, size_t dest_size, const wchar_t *src);
/**
* Converts a wide string to UTF-8.
......@@ -31,30 +62,66 @@ int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
* @param dest The buffer to receive the UTF-8 string.
* @param dest_size The size of the buffer, in bytes.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
* @param src_len The length of the string to convert.
* @return The length of the UTF-8 string, in bytes
* (not counting the NULL terminator), or < 0 for failure
*/
int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src);
int git_utf8_from_16_with_len(char *dest, size_t dest_size, const wchar_t *src, int src_len);
/**
* Converts a UTF-8 string to wide characters.
* Memory is allocated to hold the converted string.
* The caller is responsible for freeing the string with git__free.
* Converts a UTF-8 string to wide characters. Memory is allocated to hold
* the converted string. The caller is responsible for freeing the string
* with git__free.
*
* @param dest Receives a pointer to the wide string.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
* @return The length of the wide string, in characters
* (not counting the NULL terminator), or < 0 for failure
*/
int git__utf8_to_16_alloc(wchar_t **dest, const char *src);
int git_utf8_to_16_alloc(wchar_t **dest, const char *src);
/**
* Converts a wide string to UTF-8.
* Memory is allocated to hold the converted string.
* The caller is responsible for freeing the string with git__free.
* Converts a UTF-8 string to wide characters. Memory is allocated to hold
* the converted string. The caller is responsible for freeing the string
* with git__free.
*
* @param dest Receives a pointer to the wide string.
* @param src The UTF-8 string to convert.
* @param src_len The length of the string.
* @return The length of the wide string, in characters
* (not counting the NULL terminator), or < 0 for failure
*/
int git_utf8_to_16_alloc_with_len(
wchar_t **dest,
const char *src,
int src_len);
/**
* Converts a wide string to UTF-8. Memory is allocated to hold the
* converted string. The caller is responsible for freeing the string
* with git__free.
*
* @param dest Receives a pointer to the UTF-8 string.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes
* (not counting the NULL terminator), or < 0 for failure
*/
int git_utf8_from_16_alloc(char **dest, const wchar_t *src);
/**
* Converts a wide string to UTF-8. Memory is allocated to hold the
* converted string. The caller is responsible for freeing the string
* with git__free.
*
* @param dest Receives a pointer to the UTF-8 string.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
* @param src_len The length of the wide string.
* @return The length of the UTF-8 string, in bytes
* (not counting the NULL terminator), or < 0 for failure
*/
int git__utf16_to_8_alloc(char **dest, const wchar_t *src);
int git_utf8_from_16_alloc_with_len(
char **dest,
const wchar_t *src,
int src_len);
#endif
......@@ -115,7 +115,7 @@ int git_win32__file_attribute_to_stat(
/* st_size gets the UTF-8 length of the target name, in bytes,
* not counting the NULL terminator */
if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) {
if ((st->st_size = git_utf8_from_16(NULL, 0, target)) < 0) {
git_error_set(GIT_ERROR_OS, "could not convert reparse point name for '%ls'", path);
return -1;
}
......
......@@ -103,10 +103,10 @@ int cl_setenv(const char *name, const char *value)
{
wchar_t *wide_name, *wide_value = NULL;
cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0);
cl_assert(git_utf8_to_16_alloc(&wide_name, name) >= 0);
if (value) {
cl_assert(git__utf8_to_16_alloc(&wide_value, value) >= 0);
cl_assert(git_utf8_to_16_alloc(&wide_value, value) >= 0);
cl_assert(SetEnvironmentVariableW(wide_name, wide_value));
} else {
/* Windows XP returns 0 (failed) when passing NULL for lpValue when
......
......@@ -580,6 +580,17 @@ static int succeed_certificate_check(git_cert *cert, int valid, const char *host
return 0;
}
static int x509_succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(valid);
GIT_UNUSED(payload);
cl_assert_equal_s("github.com", host);
cl_assert_equal_i(GIT_CERT_X509, cert->cert_type);
return 0;
}
static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(cert);
......@@ -901,7 +912,7 @@ void test_online_clone__certificate_invalid(void)
void test_online_clone__certificate_valid(void)
{
g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
g_options.fetch_opts.callbacks.certificate_check = x509_succeed_certificate_check;
cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
}
......
......@@ -81,10 +81,10 @@ void test_stream_registration__tls(void)
cl_git_pass(git_stream_register(GIT_STREAM_TLS, NULL));
error = git_tls_stream_new(&stream, "localhost", "443");
/* We don't have TLS support enabled, or we're on Windows,
* which has no arbitrary TLS stream support.
/* We don't have TLS support enabled, or we're on Windows
* with WinHTTP, which is not actually TLS stream support.
*/
#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
#if defined(GIT_WINHTTP) || !defined(GIT_HTTPS)
cl_git_fail_with(-1, error);
#else
cl_git_pass(error);
......
......@@ -98,7 +98,7 @@ static void do_junction(const char *old, const char *new)
git_str_putc(&unparsed_buf, '\\');
subst_utf16_len = git__utf8_to_16(NULL, 0, git_str_cstr(&unparsed_buf));
subst_utf16_len = git_utf8_to_16(NULL, 0, git_str_cstr(&unparsed_buf));
subst_byte_len = subst_utf16_len * sizeof(WCHAR);
print_utf16_len = subst_utf16_len - 4;
......@@ -124,11 +124,11 @@ static void do_junction(const char *old, const char *new)
subst_utf16 = reparse_buf->ReparseBuffer.MountPoint.PathBuffer;
print_utf16 = subst_utf16 + subst_utf16_len + 1;
ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1,
ret = git_utf8_to_16(subst_utf16, subst_utf16_len + 1,
git_str_cstr(&unparsed_buf));
cl_assert_equal_i(subst_utf16_len, ret);
ret = git__utf8_to_16(print_utf16,
ret = git_utf8_to_16(print_utf16,
print_utf16_len + 1, git_str_cstr(&unparsed_buf) + 4);
cl_assert_equal_i(print_utf16_len, ret);
......
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