Introduce git_cred_default for NTLM/SPNEGO auth
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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 81ebf4d..fe4883f 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -31,13 +31,16 @@ GIT_BEGIN_DECL
/** Authentication type requested */
typedef enum {
/* git_cred_userpass_plaintext */
- GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
+ GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
/* git_cred_ssh_key */
GIT_CREDTYPE_SSH_KEY = (1u << 1),
/* 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;
/* The base structure for all credential types */
@@ -48,7 +51,7 @@ struct git_cred {
void (*free)(git_cred *cred);
};
-/* A plaintext username and password */
+/** A plaintext username and password */
typedef struct {
git_cred parent;
char *username;
@@ -84,6 +87,9 @@ typedef struct git_cred_ssh_custom {
void *sign_data;
} 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.
*
@@ -151,6 +157,14 @@ GIT_EXTERN(int) git_cred_ssh_custom_new(
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.
*
* @param cred The newly created credential object.
diff --git a/src/transports/cred.c b/src/transports/cred.c
index cc7fdab..05d2c8d 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -29,6 +29,10 @@ int git_cred_has_username(git_cred *cred)
ret = !!c->username;
break;
}
+ case GIT_CREDTYPE_DEFAULT: {
+ ret = 0;
+ break;
+ }
}
return ret;
@@ -115,6 +119,13 @@ static void ssh_custom_free(struct git_cred *cred)
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(
git_cred **cred,
const char *username,
@@ -191,3 +202,19 @@ int git_cred_ssh_custom_new(
*cred = &c->parent;
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;
+}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index f756645..673cd0f 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -52,6 +52,7 @@ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
typedef enum {
GIT_WINHTTP_AUTH_BASIC = 1,
+ GIT_WINHTTP_AUTH_NEGOTIATE = 2,
} winhttp_authmechanism_t;
typedef struct {
@@ -138,6 +139,22 @@ on_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)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -317,6 +334,11 @@ static int winhttp_stream_connect(winhttp_stream *s)
t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
apply_basic_credential(s->request, t->cred) < 0)
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
* password, use those */
@@ -361,6 +383,12 @@ static int parse_unauthorized_response(
*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;
}
diff --git a/tests/online/push.c b/tests/online/push.c
index aeb1ab4..be505c3 100644
--- a/tests/online/push.c
+++ b/tests/online/push.c
@@ -9,14 +9,17 @@
static git_repository *_repo;
+static char *_remote_url;
+
static char *_remote_ssh_key;
static char *_remote_ssh_pubkey;
static char *_remote_ssh_passphrase;
-static char *_remote_url;
static char *_remote_user;
static char *_remote_pass;
+static char *_remote_default;
+
static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
static git_remote *_remote;
@@ -47,11 +50,21 @@ static int cred_acquire_cb(
GIT_UNUSED(user_from_url);
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 (!_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");
return -1;
}
+
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)
_remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY");
_remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+ _remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
_remote = NULL;
if (_remote_url) {