summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2018-09-15 21:01:50 +0100
committerEdward Thomson <ethomson@edwardthomson.com>2018-09-15 21:01:50 +0100
commita5c9047f8bb9ecc3d09ccbf5b23cf7b24b8cae3e (patch)
treef2880ad96f6d419c49ad279b120e37ea82a56ef4
parent0ff9cb767c9af3bccd76c6f2973e169a49632c81 (diff)
downloadlibgit2-ethomson/ntlm2.tar.gz
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/features.h.in2
-rw-r--r--src/transports/auth.c2
-rw-r--r--src/transports/auth.h1
-rw-r--r--src/transports/auth_ntlm.c216
-rw-r--r--src/transports/auth_ntlm.h35
-rw-r--r--src/transports/http.c31
-rw-r--r--tests/online/clone.c15
8 files changed, 299 insertions, 7 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b2c124e21..c05458297 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -363,12 +363,12 @@ ADD_FEATURE_INFO(SSH GIT_SSH "SSH transport support")
# Optional external dependency: ntlmclient
IF (USE_NTLMCLIENT)
- SET(GIT_NTLMCLIENT 1)
+ SET(GIT_NTLM 1)
ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/ntlmclient" "${libgit2_BINARY_DIR}/deps/ntlmclient")
LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/ntlmclient")
LIST(APPEND LIBGIT2_OBJECTS "$<TARGET_OBJECTS:ntlmclient>")
ENDIF()
-ADD_FEATURE_INFO(ntlmclient GIT_NTLMCLIENT "NTLM authentication support for Unix")
+ADD_FEATURE_INFO(ntlmclient GIT_NTLM "NTLM authentication support for Unix")
# Optional external dependency: libgssapi
IF (USE_GSSAPI)
diff --git a/src/features.h.in b/src/features.h.in
index f414c5843..fab6c9e16 100644
--- a/src/features.h.in
+++ b/src/features.h.in
@@ -35,4 +35,6 @@
#cmakedefine GIT_SHA1_OPENSSL 1
#cmakedefine GIT_SHA1_MBEDTLS 1
+#cmakedefine GIT_NTLM 1
+
#endif
diff --git a/src/transports/auth.c b/src/transports/auth.c
index c8e6adb12..cf9603d4c 100644
--- a/src/transports/auth.c
+++ b/src/transports/auth.c
@@ -28,6 +28,8 @@ static int basic_next_token(
git_buf_printf(&raw, "%s:%s", cred->username, cred->password);
+ printf("authenticating: %s\n", raw.ptr);
+
if (git_buf_oom(&raw) ||
git_buf_puts(out, "Authorization: Basic ") < 0 ||
git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0 ||
diff --git a/src/transports/auth.h b/src/transports/auth.h
index 38c1b0f88..8020e8f00 100644
--- a/src/transports/auth.h
+++ b/src/transports/auth.h
@@ -16,6 +16,7 @@
typedef enum {
GIT_AUTHTYPE_BASIC = 1,
GIT_AUTHTYPE_NEGOTIATE = 2,
+ GIT_AUTHTYPE_NTLM = 4,
} git_http_authtype_t;
typedef struct git_http_auth_context git_http_auth_context;
diff --git a/src/transports/auth_ntlm.c b/src/transports/auth_ntlm.c
new file mode 100644
index 000000000..7065a1fc6
--- /dev/null
+++ b/src/transports/auth_ntlm.c
@@ -0,0 +1,216 @@
+/*
+ * 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 "common.h"
+#include "buffer.h"
+#include "auth.h"
+#include "auth_ntlm.h"
+
+#ifdef GIT_NTLM
+
+#include "ntlm.h"
+
+typedef struct {
+ git_http_auth_context parent;
+ ntlm_client *ntlm;
+ char *challenge;
+} http_auth_ntlm_context;
+
+static int ntlm_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+
+ assert(ctx && challenge);
+
+ git__free(ctx->challenge);
+
+ ctx->challenge = git__strdup(challenge);
+ GITERR_CHECK_ALLOC(ctx->challenge);
+
+ printf("ok, challenge is: %s\n", ctx->challenge);
+
+ return 0;
+}
+
+static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_cred *_cred)
+{
+ git_cred_userpass_plaintext *cred;
+ const char *sep;
+ const char *domain = NULL, *username;
+ char *parsed_domain = NULL, *parsed_username = NULL;
+ int error = 0;
+
+ assert(_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT);
+ cred = (git_cred_userpass_plaintext *)_cred;
+
+ username = cred->username;
+
+ if ((sep = strchr(username, '\\')) != NULL) {
+ parsed_domain = strndup(username, (sep - username));
+ GITERR_CHECK_ALLOC(parsed_domain);
+
+ parsed_username = strdup(sep + 1);
+ GITERR_CHECK_ALLOC(parsed_username);
+
+ domain = parsed_domain;
+ username = parsed_username;
+ }
+
+ if (ntlm_client_set_credentials(ctx->ntlm,
+ username, domain, cred->password) < 0) {
+ giterr_set(GITERR_NET, "could not set credentials: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ error = -1;
+ goto done;
+ }
+
+ printf("woo, my ntlm creds are %s\\%s / %s\n", domain ? domain : "(none)", username, cred->password);
+
+done:
+ git__free(parsed_domain);
+ git__free(parsed_username);
+ return error;
+}
+
+static int ntlm_next_token(
+ git_buf *buf,
+ git_http_auth_context *c,
+ git_cred *cred)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+ git_buf input_buf = GIT_BUF_INIT;
+ const unsigned char *msg;
+ size_t challenge_len, msg_len;
+ int error = -1;
+
+ printf("next token...\n");
+
+ assert(buf && ctx && ctx->ntlm);
+
+ challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
+
+ if (cred && (error = ntlm_set_credentials(ctx, cred)) < 0)
+ goto done;
+
+ if (challenge_len < 4) {
+ giterr_set(GITERR_NET, "no ntlm challenge sent from server");
+ goto done;
+ } else if (challenge_len == 4) {
+ if (memcmp(ctx->challenge, "NTLM", 4) != 0) {
+ giterr_set(GITERR_NET, "server did not request NTLM");
+ goto done;
+ }
+
+ if (ntlm_client_negotiate(&msg, &msg_len, ctx->ntlm) != 0) {
+ giterr_set(GITERR_NET, "ntlm authentication failed: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ goto done;
+ }
+ } else {
+ if (memcmp(ctx->challenge, "NTLM ", 5) != 0) {
+ giterr_set(GITERR_NET, "challenge from server was not NTLM");
+ goto done;
+ }
+
+ if (git_buf_decode_base64(&input_buf,
+ ctx->challenge + 5, challenge_len - 5) < 0) {
+ giterr_set(GITERR_NET, "invalid NTLM challenge from server");
+ goto done;
+ }
+
+ if (ntlm_client_set_challenge(ctx->ntlm,
+ (const unsigned char *)input_buf.ptr, input_buf.size) != 0) {
+ giterr_set(GITERR_NET, "ntlm challenge failed: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ goto done;
+ }
+
+ if (ntlm_client_response(&msg, &msg_len, ctx->ntlm) != 0) {
+ giterr_set(GITERR_NET, "ntlm authentication failed: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ goto done;
+ }
+ }
+
+ git_buf_puts(buf, "Authorization: NTLM ");
+ git_buf_encode_base64(buf, (const char *)msg, msg_len);
+ git_buf_puts(buf, "\r\n");
+
+ printf("RESPONSE: %s\n", buf->ptr);
+
+ if (git_buf_oom(buf))
+ goto done;
+
+ error = 0;
+
+done:
+ return error;
+}
+
+static void ntlm_context_free(git_http_auth_context *c)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+
+ ntlm_client_free(ctx->ntlm);
+ git__free(ctx->challenge);
+ git__free(ctx);
+}
+
+static int ntlm_init_context(
+ http_auth_ntlm_context *ctx,
+ const gitno_connection_data *connection_data)
+{
+ if ((ctx->ntlm = ntlm_client_init(NTLM_CLIENT_DEFAULTS)) == NULL) {
+ giterr_set_oom();
+ return -1;
+ }
+
+ if (ntlm_client_set_target(ctx->ntlm, connection_data->host) < 0) {
+ giterr_set(GITERR_NET, "failed to initialize NTLM: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_http_auth_ntlm(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data)
+{
+ http_auth_ntlm_context *ctx;
+
+ GIT_UNUSED(connection_data);
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_ntlm_context));
+ GITERR_CHECK_ALLOC(ctx);
+
+ if (ntlm_init_context(ctx, connection_data) < 0) {
+ git__free(ctx);
+ return -1;
+ }
+
+ printf("--------------------------------------------------------\n");
+ printf("starting ntlm?\n");
+
+ ctx->parent.type = GIT_AUTHTYPE_NTLM;
+ ctx->parent.credtypes = GIT_CREDTYPE_USERPASS_PLAINTEXT;
+ ctx->parent.set_challenge = ntlm_set_challenge;
+ ctx->parent.next_token = ntlm_next_token;
+ ctx->parent.free = ntlm_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ return 0;
+}
+
+#endif /* GIT_NTLM */
diff --git a/src/transports/auth_ntlm.h b/src/transports/auth_ntlm.h
new file mode 100644
index 000000000..b5ef1e384
--- /dev/null
+++ b/src/transports/auth_ntlm.h
@@ -0,0 +1,35 @@
+/*
+ * 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_transports_auth_ntlm_h__
+#define INCLUDE_transports_auth_ntlm_h__
+
+#include "git2.h"
+#include "auth.h"
+
+#ifdef GIT_NTLM
+
+#if defined(GIT_OPENSSL)
+# define CRYPT_OPENSSL
+#elif defined(GIT_MBEDTLS)
+# define CRYPT_MBEDTLS
+#elif defined(GIT_SECURE_TRANSPORT)
+# define CRYPT_COMMONCRYPTO
+#endif
+
+extern int git_http_auth_ntlm(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+#else
+
+#define git_http_auth_ntlm git_http_auth_dummy
+
+#endif /* GIT_NTLM */
+
+#endif
+
diff --git a/src/transports/http.c b/src/transports/http.c
index c0e0d31e3..6fb3e7ad1 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -19,12 +19,14 @@
#include "auth.h"
#include "http.h"
#include "auth_negotiate.h"
+#include "auth_ntlm.h"
#include "streams/tls.h"
#include "streams/socket.h"
#include "streams/curl.h"
git_http_auth_scheme auth_schemes[] = {
{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, 0, git_http_auth_negotiate },
+ { GIT_AUTHTYPE_NTLM, "NTLM", GIT_CREDTYPE_USERPASS_PLAINTEXT, 0, git_http_auth_ntlm },
{ GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, 1, git_http_auth_basic },
};
@@ -154,9 +156,15 @@ static int auth_context_match(
}
}
+ if(!scheme) printf("no scheme for that...\n");
+
if (!scheme)
return 0;
+ printf("i have a scheme: %p %s\n", scheme, scheme->name);
+ printf("init_context: %p (dummy is %p, ntlm is %p)\n", scheme->init_context, git_http_auth_dummy, git_http_auth_ntlm);
+
+
/* See if authentication has already started for this scheme */
git_vector_foreach(&t->auth_contexts, i, c) {
if (c->type == scheme->type) {
@@ -166,14 +174,19 @@ static int auth_context_match(
}
if (!context) {
+ printf("starting context...\n");
if (scheme->init_context(&context, &t->connection_data) < 0)
return -1;
+
+ printf("context is: %p\n", context); if (0) ;
+
else if (!context)
return 0;
else if (git_vector_insert(&t->auth_contexts, context) < 0)
return -1;
}
+ printf("done: %p\n", context);
*out = context;
return 0;
@@ -289,6 +302,8 @@ static int parse_authenticate_response(
size_t i;
git_vector_foreach(www_authenticate, i, challenge) {
+ printf("challenge: %s\n", challenge);
+
if (auth_context_match(&context, t, challenge_match, challenge) < 0)
return -1;
else if (!context)
@@ -298,6 +313,8 @@ static int parse_authenticate_response(
context->set_challenge(context, challenge) < 0)
return -1;
+ printf("context type: %x / cred types: %x\n", context->type, context->credtypes);
+
/*
* Record both the supported mechs on the server and the
* corresponding credential types so that we know what mech
@@ -307,6 +324,8 @@ static int parse_authenticate_response(
*allowed_types |= context->credtypes;
}
+ printf("server types: %x\n", t->auth_server_types);
+
return 0;
}
@@ -381,7 +400,9 @@ static int on_headers_complete(http_parser *parser)
http_subtransport *t = ctx->t;
http_stream *s = ctx->s;
git_buf buf = GIT_BUF_INIT;
- int error = 0, no_callback = 0, allowed_auth_types = 0;
+ int error = 0, no_callback = 0, allowed_credtypes = 0;
+
+ printf("------> %d ------->\n", parser->status_code);
/* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */
@@ -394,7 +415,7 @@ static int on_headers_complete(http_parser *parser)
* complete.)
*/
if (parse_authenticate_response(&t->www_authenticate, t,
- &allowed_auth_types) < 0)
+ &allowed_credtypes) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
/* Check for an authentication failure. */
@@ -402,7 +423,7 @@ static int on_headers_complete(http_parser *parser)
if (!t->owner->cred_acquire_cb) {
no_callback = 1;
} else {
- if (allowed_auth_types) {
+ if (allowed_credtypes) {
if (t->cred) {
t->cred->free(t->cred);
t->cred = NULL;
@@ -411,7 +432,7 @@ static int on_headers_complete(http_parser *parser)
error = t->owner->cred_acquire_cb(&t->cred,
t->owner->url,
t->connection_data.user,
- allowed_auth_types,
+ allowed_credtypes,
t->owner->cred_acquire_payload);
if (error == GIT_PASSTHROUGH) {
@@ -422,7 +443,7 @@ static int on_headers_complete(http_parser *parser)
} else {
assert(t->cred);
- if (!(t->cred->credtype & allowed_auth_types)) {
+ if (!(t->cred->credtype & allowed_credtypes)) {
giterr_set(GITERR_NET, "credentials callback returned an invalid cred type");
return t->parse_error = PARSE_ERROR_GENERIC;
}
diff --git a/tests/online/clone.c b/tests/online/clone.c
index de66238e3..d705ac26e 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -372,6 +372,21 @@ void test_online_clone__bitbucket_style(void)
cl_fixture_cleanup("./foo");
}
+void test_online_clone__ntlm(void)
+{
+ git_cred_userpass_payload user_pass = {
+ "zapp\\ethomson", "3,mFBVcrWi7C&r+a"
+ };
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = "https://localhost:8888/";
+ g_options.fetch_opts.callbacks.credentials = git_cred_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
+
+ cl_git_pass(git_clone(&g_repo, "https://zapp.edwardthomson.com/DefaultCollection/DemoProject/_git/TestRepository", "/tmp/test", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+}
+
void test_online_clone__bitbucket_uses_creds_in_url(void)
{
git_cred_userpass_payload user_pass = {