Commit 84efffc3 by Edward Thomson

Introduce git_cred_default for NTLM/SPNEGO auth

parent 80fc7d6b
...@@ -31,13 +31,16 @@ GIT_BEGIN_DECL ...@@ -31,13 +31,16 @@ GIT_BEGIN_DECL
/** Authentication type requested */ /** Authentication type requested */
typedef enum { typedef enum {
/* git_cred_userpass_plaintext */ /* git_cred_userpass_plaintext */
GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0), GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
/* git_cred_ssh_key */ /* git_cred_ssh_key */
GIT_CREDTYPE_SSH_KEY = (1u << 1), GIT_CREDTYPE_SSH_KEY = (1u << 1),
/* git_cred_ssh_custom */ /* git_cred_ssh_custom */
GIT_CREDTYPE_SSH_CUSTOM = (1u << 2), GIT_CREDTYPE_SSH_CUSTOM = (1u << 2),
/* git_cred_default */
GIT_CREDTYPE_DEFAULT = (1u << 3),
} git_credtype_t; } git_credtype_t;
/* The base structure for all credential types */ /* The base structure for all credential types */
...@@ -48,7 +51,7 @@ struct git_cred { ...@@ -48,7 +51,7 @@ struct git_cred {
void (*free)(git_cred *cred); void (*free)(git_cred *cred);
}; };
/* A plaintext username and password */ /** A plaintext username and password */
typedef struct { typedef struct {
git_cred parent; git_cred parent;
char *username; char *username;
...@@ -84,6 +87,9 @@ typedef struct git_cred_ssh_custom { ...@@ -84,6 +87,9 @@ typedef struct git_cred_ssh_custom {
void *sign_data; void *sign_data;
} git_cred_ssh_custom; } git_cred_ssh_custom;
/** A key for NTLM/Kerberos "default" credentials */
typedef struct git_cred git_cred_default;
/** /**
* Check whether a credential object contains username information. * Check whether a credential object contains username information.
* *
...@@ -151,6 +157,14 @@ GIT_EXTERN(int) git_cred_ssh_custom_new( ...@@ -151,6 +157,14 @@ GIT_EXTERN(int) git_cred_ssh_custom_new(
void *sign_data); void *sign_data);
/** /**
* Create a "default" credential usable for Negotiate mechanisms like NTLM
* or Kerberos authentication.
*
* @return 0 for success or an error code for failure
*/
GIT_EXTERN(int) git_cred_default_new(git_cred **out);
/**
* Signature of a function which acquires a credential object. * Signature of a function which acquires a credential object.
* *
* @param cred The newly created credential object. * @param cred The newly created credential object.
......
...@@ -29,6 +29,10 @@ int git_cred_has_username(git_cred *cred) ...@@ -29,6 +29,10 @@ int git_cred_has_username(git_cred *cred)
ret = !!c->username; ret = !!c->username;
break; break;
} }
case GIT_CREDTYPE_DEFAULT: {
ret = 0;
break;
}
} }
return ret; return ret;
...@@ -115,6 +119,13 @@ static void ssh_custom_free(struct git_cred *cred) ...@@ -115,6 +119,13 @@ static void ssh_custom_free(struct git_cred *cred)
git__free(c); git__free(c);
} }
static void default_free(struct git_cred *cred)
{
git_cred_default *c = (git_cred_default *)cred;
git__free(c);
}
int git_cred_ssh_key_new( int git_cred_ssh_key_new(
git_cred **cred, git_cred **cred,
const char *username, const char *username,
...@@ -191,3 +202,19 @@ int git_cred_ssh_custom_new( ...@@ -191,3 +202,19 @@ int git_cred_ssh_custom_new(
*cred = &c->parent; *cred = &c->parent;
return 0; return 0;
} }
int git_cred_default_new(git_cred **cred)
{
git_cred_default *c;
assert(cred);
c = git__calloc(1, sizeof(git_cred_default));
GITERR_CHECK_ALLOC(c);
c->credtype = GIT_CREDTYPE_DEFAULT;
c->free = default_free;
*cred = c;
return 0;
}
...@@ -52,6 +52,7 @@ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | ...@@ -52,6 +52,7 @@ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
typedef enum { typedef enum {
GIT_WINHTTP_AUTH_BASIC = 1, GIT_WINHTTP_AUTH_BASIC = 1,
GIT_WINHTTP_AUTH_NEGOTIATE = 2,
} winhttp_authmechanism_t; } winhttp_authmechanism_t;
typedef struct { typedef struct {
...@@ -138,6 +139,22 @@ on_error: ...@@ -138,6 +139,22 @@ on_error:
return error; return error;
} }
static int apply_default_credentials(HINTERNET request)
{
/* If we are explicitly asked to deliver default credentials, turn set
* the security level to low which will guarantee they are delivered.
* The default is "medium" which applies to the intranet and sounds
* like it would correspond to Internet Explorer security zones, but
* in fact does not.
*/
DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
return -1;
return 0;
}
static int winhttp_stream_connect(winhttp_stream *s) static int winhttp_stream_connect(winhttp_stream *s)
{ {
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
...@@ -317,6 +334,11 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -317,6 +334,11 @@ static int winhttp_stream_connect(winhttp_stream *s)
t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC && t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
apply_basic_credential(s->request, t->cred) < 0) apply_basic_credential(s->request, t->cred) < 0)
goto on_error; goto on_error;
else if (t->cred &&
t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE &&
apply_default_credentials(s->request) < 0)
goto on_error;
/* If no other credentials have been applied and the URL has username and /* If no other credentials have been applied and the URL has username and
* password, use those */ * password, use those */
...@@ -361,6 +383,12 @@ static int parse_unauthorized_response( ...@@ -361,6 +383,12 @@ static int parse_unauthorized_response(
*auth_mechanism = GIT_WINHTTP_AUTH_BASIC; *auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
} }
if ((WINHTTP_AUTH_SCHEME_NTLM & supported) ||
(WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) {
*allowed_types |= GIT_CREDTYPE_DEFAULT;
*auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE;
}
return 0; return 0;
} }
......
...@@ -9,14 +9,17 @@ ...@@ -9,14 +9,17 @@
static git_repository *_repo; static git_repository *_repo;
static char *_remote_url;
static char *_remote_ssh_key; static char *_remote_ssh_key;
static char *_remote_ssh_pubkey; static char *_remote_ssh_pubkey;
static char *_remote_ssh_passphrase; static char *_remote_ssh_passphrase;
static char *_remote_url;
static char *_remote_user; static char *_remote_user;
static char *_remote_pass; static char *_remote_pass;
static char *_remote_default;
static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *); static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
static git_remote *_remote; static git_remote *_remote;
...@@ -47,11 +50,21 @@ static int cred_acquire_cb( ...@@ -47,11 +50,21 @@ static int cred_acquire_cb(
GIT_UNUSED(user_from_url); GIT_UNUSED(user_from_url);
GIT_UNUSED(payload); GIT_UNUSED(payload);
if (GIT_CREDTYPE_DEFAULT & allowed_types) {
if (!_remote_default) {
printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
return -1;
}
return git_cred_default_new(cred);
}
if (GIT_CREDTYPE_SSH_KEY & allowed_types) { if (GIT_CREDTYPE_SSH_KEY & allowed_types) {
if (!_remote_user || !_remote_ssh_pubkey || !_remote_ssh_key || !_remote_ssh_passphrase) { if (!_remote_user || !_remote_ssh_pubkey || !_remote_ssh_key || !_remote_ssh_passphrase) {
printf("GITTEST_REMOTE_USER, GITTEST_REMOTE_SSH_PUBKEY, GITTEST_REMOTE_SSH_KEY and GITTEST_REMOTE_SSH_PASSPHRASE must be set\n"); printf("GITTEST_REMOTE_USER, GITTEST_REMOTE_SSH_PUBKEY, GITTEST_REMOTE_SSH_KEY and GITTEST_REMOTE_SSH_PASSPHRASE must be set\n");
return -1; return -1;
} }
return git_cred_ssh_key_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase); return git_cred_ssh_key_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase);
} }
...@@ -298,6 +311,7 @@ void test_online_push__initialize(void) ...@@ -298,6 +311,7 @@ void test_online_push__initialize(void)
_remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY"); _remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY");
_remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
_remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
_remote = NULL; _remote = NULL;
if (_remote_url) { if (_remote_url) {
......
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