]> git.proxmox.com Git - libgit2.git/blob - src/transports/auth_ntlm.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / src / transports / auth_ntlm.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "git2.h"
9 #include "common.h"
10 #include "buffer.h"
11 #include "auth.h"
12 #include "auth_ntlm.h"
13 #include "git2/sys/credential.h"
14
15 #ifdef GIT_NTLM
16
17 #include "ntlmclient.h"
18
19 typedef struct {
20 git_http_auth_context parent;
21 ntlm_client *ntlm;
22 char *challenge;
23 bool complete;
24 } http_auth_ntlm_context;
25
26 static int ntlm_set_challenge(
27 git_http_auth_context *c,
28 const char *challenge)
29 {
30 http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
31
32 GIT_ASSERT_ARG(ctx);
33 GIT_ASSERT_ARG(challenge);
34
35 git__free(ctx->challenge);
36
37 ctx->challenge = git__strdup(challenge);
38 GIT_ERROR_CHECK_ALLOC(ctx->challenge);
39
40 return 0;
41 }
42
43 static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred)
44 {
45 git_credential_userpass_plaintext *cred;
46 const char *sep, *username;
47 char *domain = NULL, *domainuser = NULL;
48 int error = 0;
49
50 GIT_ASSERT(_cred->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT);
51 cred = (git_credential_userpass_plaintext *)_cred;
52
53 if ((sep = strchr(cred->username, '\\')) != NULL) {
54 domain = git__strndup(cred->username, (sep - cred->username));
55 GIT_ERROR_CHECK_ALLOC(domain);
56
57 domainuser = git__strdup(sep + 1);
58 GIT_ERROR_CHECK_ALLOC(domainuser);
59
60 username = domainuser;
61 } else {
62 username = cred->username;
63 }
64
65 if (ntlm_client_set_credentials(ctx->ntlm,
66 username, domain, cred->password) < 0) {
67 git_error_set(GIT_ERROR_NET, "could not set credentials: %s",
68 ntlm_client_errmsg(ctx->ntlm));
69 error = -1;
70 goto done;
71 }
72
73 done:
74 git__free(domain);
75 git__free(domainuser);
76 return error;
77 }
78
79 static int ntlm_next_token(
80 git_buf *buf,
81 git_http_auth_context *c,
82 git_credential *cred)
83 {
84 http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
85 git_buf input_buf = GIT_BUF_INIT;
86 const unsigned char *msg;
87 size_t challenge_len, msg_len;
88 int error = GIT_EAUTH;
89
90 GIT_ASSERT_ARG(buf);
91 GIT_ASSERT_ARG(ctx);
92
93 GIT_ASSERT(ctx->ntlm);
94
95 challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
96
97 if (ctx->complete)
98 ntlm_client_reset(ctx->ntlm);
99
100 /*
101 * Set us complete now since it's the default case; the one
102 * incomplete case (successfully created a client request)
103 * will explicitly set that it requires a second step.
104 */
105 ctx->complete = true;
106
107 if (cred && ntlm_set_credentials(ctx, cred) != 0)
108 goto done;
109
110 if (challenge_len < 4) {
111 git_error_set(GIT_ERROR_NET, "no ntlm challenge sent from server");
112 goto done;
113 } else if (challenge_len == 4) {
114 if (memcmp(ctx->challenge, "NTLM", 4) != 0) {
115 git_error_set(GIT_ERROR_NET, "server did not request NTLM");
116 goto done;
117 }
118
119 if (ntlm_client_negotiate(&msg, &msg_len, ctx->ntlm) != 0) {
120 git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s",
121 ntlm_client_errmsg(ctx->ntlm));
122 goto done;
123 }
124
125 ctx->complete = false;
126 } else {
127 if (memcmp(ctx->challenge, "NTLM ", 5) != 0) {
128 git_error_set(GIT_ERROR_NET, "challenge from server was not NTLM");
129 goto done;
130 }
131
132 if (git_buf_decode_base64(&input_buf,
133 ctx->challenge + 5, challenge_len - 5) < 0) {
134 git_error_set(GIT_ERROR_NET, "invalid NTLM challenge from server");
135 goto done;
136 }
137
138 if (ntlm_client_set_challenge(ctx->ntlm,
139 (const unsigned char *)input_buf.ptr, input_buf.size) != 0) {
140 git_error_set(GIT_ERROR_NET, "ntlm challenge failed: %s",
141 ntlm_client_errmsg(ctx->ntlm));
142 goto done;
143 }
144
145 if (ntlm_client_response(&msg, &msg_len, ctx->ntlm) != 0) {
146 git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s",
147 ntlm_client_errmsg(ctx->ntlm));
148 goto done;
149 }
150 }
151
152 git_buf_puts(buf, "NTLM ");
153 git_buf_encode_base64(buf, (const char *)msg, msg_len);
154
155 if (git_buf_oom(buf))
156 goto done;
157
158 error = 0;
159
160 done:
161 git_buf_dispose(&input_buf);
162 return error;
163 }
164
165 static int ntlm_is_complete(git_http_auth_context *c)
166 {
167 http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
168
169 GIT_ASSERT_ARG(ctx);
170 return (ctx->complete == true);
171 }
172
173 static void ntlm_context_free(git_http_auth_context *c)
174 {
175 http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
176
177 ntlm_client_free(ctx->ntlm);
178 git__free(ctx->challenge);
179 git__free(ctx);
180 }
181
182 static int ntlm_init_context(
183 http_auth_ntlm_context *ctx,
184 const git_net_url *url)
185 {
186 GIT_UNUSED(url);
187
188 if ((ctx->ntlm = ntlm_client_init(NTLM_CLIENT_DEFAULTS)) == NULL) {
189 git_error_set_oom();
190 return -1;
191 }
192
193 return 0;
194 }
195
196 int git_http_auth_ntlm(
197 git_http_auth_context **out,
198 const git_net_url *url)
199 {
200 http_auth_ntlm_context *ctx;
201
202 GIT_UNUSED(url);
203
204 *out = NULL;
205
206 ctx = git__calloc(1, sizeof(http_auth_ntlm_context));
207 GIT_ERROR_CHECK_ALLOC(ctx);
208
209 if (ntlm_init_context(ctx, url) < 0) {
210 git__free(ctx);
211 return -1;
212 }
213
214 ctx->parent.type = GIT_HTTP_AUTH_NTLM;
215 ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT;
216 ctx->parent.connection_affinity = 1;
217 ctx->parent.set_challenge = ntlm_set_challenge;
218 ctx->parent.next_token = ntlm_next_token;
219 ctx->parent.is_complete = ntlm_is_complete;
220 ctx->parent.free = ntlm_context_free;
221
222 *out = (git_http_auth_context *)ctx;
223
224 return 0;
225 }
226
227 #endif /* GIT_NTLM */