diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2019-03-07 16:57:11 +0000 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2019-06-10 19:58:22 +0100 |
commit | 3192e3c970f490164852dd76f928b753f821e40d (patch) | |
tree | 1474ac300d51bbf943e945a001991fabd631c616 | |
parent | a7f65f03bd1150f48398eaf48703f90025b41342 (diff) | |
download | libgit2-3192e3c970f490164852dd76f928b753f821e40d.tar.gz |
http: provide an NTLM authentication provider
-rw-r--r-- | src/features.h.in | 1 | ||||
-rw-r--r-- | src/transports/auth.h | 1 | ||||
-rw-r--r-- | src/transports/auth_ntlm.c | 223 | ||||
-rw-r--r-- | src/transports/auth_ntlm.h | 35 | ||||
-rw-r--r-- | src/transports/http.c | 2 |
5 files changed, 262 insertions, 0 deletions
diff --git a/src/features.h.in b/src/features.h.in index 63800f84d..f2931cb11 100644 --- a/src/features.h.in +++ b/src/features.h.in @@ -25,6 +25,7 @@ #cmakedefine GIT_SSH 1 #cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1 +#cmakedefine GIT_NTLM 1 #cmakedefine GIT_GSSAPI 1 #cmakedefine GIT_WINHTTP 1 #cmakedefine GIT_NTLM 1 diff --git a/src/transports/auth.h b/src/transports/auth.h index 9ead5583f..396e7931b 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..55320e95c --- /dev/null +++ b/src/transports/auth_ntlm.c @@ -0,0 +1,223 @@ +/* + * 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; + bool complete; +} 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); + GIT_ERROR_CHECK_ALLOC(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, *username; + char *domain = NULL, *domainuser = NULL; + int error = 0; + + assert(_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT); + cred = (git_cred_userpass_plaintext *)_cred; + + if ((sep = strchr(cred->username, '\\')) != NULL) { + domain = strndup(cred->username, (sep - cred->username)); + GIT_ERROR_CHECK_ALLOC(domain); + + domainuser = strdup(sep + 1); + GIT_ERROR_CHECK_ALLOC(domainuser); + + username = domainuser; + } else { + username = cred->username; + } + + if (ntlm_client_set_credentials(ctx->ntlm, + username, domain, cred->password) < 0) { + git_error_set(GIT_ERROR_NET, "could not set credentials: %s", + ntlm_client_errmsg(ctx->ntlm)); + error = -1; + goto done; + } + +done: + git__free(domain); + git__free(domainuser); + return error; +} + +static int ntlm_next_token( + git_buf *buf, + git_http_auth_context *c, + const char *header_name, + 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; + + assert(buf && ctx && ctx->ntlm); + + challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; + + if (ctx->complete) + ntlm_client_reset(ctx->ntlm); + + /* + * Set us complete now since it's the default case; the one + * incomplete case (successfully created a client request) + * will explicitly set that it requires a second step. + */ + ctx->complete = true; + + if (cred && ntlm_set_credentials(ctx, cred) != 0) + goto done; + + if (challenge_len < 4) { + git_error_set(GIT_ERROR_NET, "no ntlm challenge sent from server"); + goto done; + } else if (challenge_len == 4) { + if (memcmp(ctx->challenge, "NTLM", 4) != 0) { + git_error_set(GIT_ERROR_NET, "server did not request NTLM"); + goto done; + } + + if (ntlm_client_negotiate(&msg, &msg_len, ctx->ntlm) != 0) { + git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s", + ntlm_client_errmsg(ctx->ntlm)); + goto done; + } + + ctx->complete = false; + } else { + if (memcmp(ctx->challenge, "NTLM ", 5) != 0) { + git_error_set(GIT_ERROR_NET, "challenge from server was not NTLM"); + goto done; + } + + if (git_buf_decode_base64(&input_buf, + ctx->challenge + 5, challenge_len - 5) < 0) { + git_error_set(GIT_ERROR_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) { + git_error_set(GIT_ERROR_NET, "ntlm challenge failed: %s", + ntlm_client_errmsg(ctx->ntlm)); + goto done; + } + + if (ntlm_client_response(&msg, &msg_len, ctx->ntlm) != 0) { + git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s", + ntlm_client_errmsg(ctx->ntlm)); + goto done; + } + } + + git_buf_printf(buf, "%s: NTLM ", header_name); + git_buf_encode_base64(buf, (const char *)msg, msg_len); + git_buf_puts(buf, "\r\n"); + + if (git_buf_oom(buf)) + goto done; + + error = 0; + +done: + git_buf_dispose(&input_buf); + return error; +} + +static int ntlm_is_complete(git_http_auth_context *c) +{ + http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; + + assert(ctx); + return (ctx->complete == true); +} + +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 git_net_url *url) +{ + GIT_UNUSED(url); + + if ((ctx->ntlm = ntlm_client_init(NTLM_CLIENT_DEFAULTS)) == NULL) { + git_error_set_oom(); + return -1; + } + + return 0; +} + +int git_http_auth_ntlm( + git_http_auth_context **out, + const git_net_url *url) +{ + http_auth_ntlm_context *ctx; + + GIT_UNUSED(url); + + *out = NULL; + + ctx = git__calloc(1, sizeof(http_auth_ntlm_context)); + GIT_ERROR_CHECK_ALLOC(ctx); + + if (ntlm_init_context(ctx, url) < 0) { + git__free(ctx); + return -1; + } + + 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.is_complete = ntlm_is_complete; + 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..5b42b2b8e --- /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 git_net_url *url); + +#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 6ac13ca3c..745da9252 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -20,11 +20,13 @@ #include "auth.h" #include "http.h" #include "auth_negotiate.h" +#include "auth_ntlm.h" #include "streams/tls.h" #include "streams/socket.h" git_http_auth_scheme auth_schemes[] = { { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate }, + { GIT_AUTHTYPE_NTLM, "NTLM", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_ntlm }, { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic }, }; |