1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 2012 - 2022, Daniel Stenberg, <[email protected]>, et al.
|
---|
9 | *
|
---|
10 | * This software is licensed as described in the file COPYING, which
|
---|
11 | * you should have received as part of this distribution. The terms
|
---|
12 | * are also available at https://curl.se/docs/copyright.html.
|
---|
13 | *
|
---|
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
15 | * copies of the Software, and permit persons to whom the Software is
|
---|
16 | * furnished to do so, under the terms of the COPYING file.
|
---|
17 | *
|
---|
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
19 | * KIND, either express or implied.
|
---|
20 | *
|
---|
21 | * SPDX-License-Identifier: curl
|
---|
22 | *
|
---|
23 | * RFC2195 CRAM-MD5 authentication
|
---|
24 | * RFC2617 Basic and Digest Access Authentication
|
---|
25 | * RFC2831 DIGEST-MD5 authentication
|
---|
26 | * RFC4422 Simple Authentication and Security Layer (SASL)
|
---|
27 | * RFC4616 PLAIN authentication
|
---|
28 | * RFC5802 SCRAM-SHA-1 authentication
|
---|
29 | * RFC7677 SCRAM-SHA-256 authentication
|
---|
30 | * RFC6749 OAuth 2.0 Authorization Framework
|
---|
31 | * RFC7628 A Set of SASL Mechanisms for OAuth
|
---|
32 | * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
|
---|
33 | *
|
---|
34 | ***************************************************************************/
|
---|
35 |
|
---|
36 | #include "curl_setup.h"
|
---|
37 |
|
---|
38 | #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
|
---|
39 | !defined(CURL_DISABLE_POP3)
|
---|
40 |
|
---|
41 | #include <curl/curl.h>
|
---|
42 | #include "urldata.h"
|
---|
43 |
|
---|
44 | #include "curl_base64.h"
|
---|
45 | #include "curl_md5.h"
|
---|
46 | #include "vauth/vauth.h"
|
---|
47 | #include "cfilters.h"
|
---|
48 | #include "vtls/vtls.h"
|
---|
49 | #include "curl_hmac.h"
|
---|
50 | #include "curl_sasl.h"
|
---|
51 | #include "warnless.h"
|
---|
52 | #include "strtok.h"
|
---|
53 | #include "sendf.h"
|
---|
54 | /* The last 3 #include files should be in this order */
|
---|
55 | #include "curl_printf.h"
|
---|
56 | #include "curl_memory.h"
|
---|
57 | #include "memdebug.h"
|
---|
58 |
|
---|
59 | /* Supported mechanisms */
|
---|
60 | static const struct {
|
---|
61 | const char *name; /* Name */
|
---|
62 | size_t len; /* Name length */
|
---|
63 | unsigned short bit; /* Flag bit */
|
---|
64 | } mechtable[] = {
|
---|
65 | { "LOGIN", 5, SASL_MECH_LOGIN },
|
---|
66 | { "PLAIN", 5, SASL_MECH_PLAIN },
|
---|
67 | { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 },
|
---|
68 | { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 },
|
---|
69 | { "GSSAPI", 6, SASL_MECH_GSSAPI },
|
---|
70 | { "EXTERNAL", 8, SASL_MECH_EXTERNAL },
|
---|
71 | { "NTLM", 4, SASL_MECH_NTLM },
|
---|
72 | { "XOAUTH2", 7, SASL_MECH_XOAUTH2 },
|
---|
73 | { "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER },
|
---|
74 | { "SCRAM-SHA-1", 11, SASL_MECH_SCRAM_SHA_1 },
|
---|
75 | { "SCRAM-SHA-256",13, SASL_MECH_SCRAM_SHA_256 },
|
---|
76 | { ZERO_NULL, 0, 0 }
|
---|
77 | };
|
---|
78 |
|
---|
79 | /*
|
---|
80 | * Curl_sasl_cleanup()
|
---|
81 | *
|
---|
82 | * This is used to cleanup any libraries or curl modules used by the sasl
|
---|
83 | * functions.
|
---|
84 | *
|
---|
85 | * Parameters:
|
---|
86 | *
|
---|
87 | * conn [in] - The connection data.
|
---|
88 | * authused [in] - The authentication mechanism used.
|
---|
89 | */
|
---|
90 | void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused)
|
---|
91 | {
|
---|
92 | (void)conn;
|
---|
93 | (void)authused;
|
---|
94 |
|
---|
95 | #if defined(USE_KERBEROS5)
|
---|
96 | /* Cleanup the gssapi structure */
|
---|
97 | if(authused == SASL_MECH_GSSAPI) {
|
---|
98 | Curl_auth_cleanup_gssapi(&conn->krb5);
|
---|
99 | }
|
---|
100 | #endif
|
---|
101 |
|
---|
102 | #if defined(USE_GSASL)
|
---|
103 | /* Cleanup the GSASL structure */
|
---|
104 | if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) {
|
---|
105 | Curl_auth_gsasl_cleanup(&conn->gsasl);
|
---|
106 | }
|
---|
107 | #endif
|
---|
108 |
|
---|
109 | #if defined(USE_NTLM)
|
---|
110 | /* Cleanup the NTLM structure */
|
---|
111 | if(authused == SASL_MECH_NTLM) {
|
---|
112 | Curl_auth_cleanup_ntlm(&conn->ntlm);
|
---|
113 | }
|
---|
114 | #endif
|
---|
115 | }
|
---|
116 |
|
---|
117 | /*
|
---|
118 | * Curl_sasl_decode_mech()
|
---|
119 | *
|
---|
120 | * Convert a SASL mechanism name into a token.
|
---|
121 | *
|
---|
122 | * Parameters:
|
---|
123 | *
|
---|
124 | * ptr [in] - The mechanism string.
|
---|
125 | * maxlen [in] - Maximum mechanism string length.
|
---|
126 | * len [out] - If not NULL, effective name length.
|
---|
127 | *
|
---|
128 | * Returns the SASL mechanism token or 0 if no match.
|
---|
129 | */
|
---|
130 | unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen,
|
---|
131 | size_t *len)
|
---|
132 | {
|
---|
133 | unsigned int i;
|
---|
134 | char c;
|
---|
135 |
|
---|
136 | for(i = 0; mechtable[i].name; i++) {
|
---|
137 | if(maxlen >= mechtable[i].len &&
|
---|
138 | !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
|
---|
139 | if(len)
|
---|
140 | *len = mechtable[i].len;
|
---|
141 |
|
---|
142 | if(maxlen == mechtable[i].len)
|
---|
143 | return mechtable[i].bit;
|
---|
144 |
|
---|
145 | c = ptr[mechtable[i].len];
|
---|
146 | if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
|
---|
147 | return mechtable[i].bit;
|
---|
148 | }
|
---|
149 | }
|
---|
150 |
|
---|
151 | return 0;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /*
|
---|
155 | * Curl_sasl_parse_url_auth_option()
|
---|
156 | *
|
---|
157 | * Parse the URL login options.
|
---|
158 | */
|
---|
159 | CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
|
---|
160 | const char *value, size_t len)
|
---|
161 | {
|
---|
162 | CURLcode result = CURLE_OK;
|
---|
163 | size_t mechlen;
|
---|
164 |
|
---|
165 | if(!len)
|
---|
166 | return CURLE_URL_MALFORMAT;
|
---|
167 |
|
---|
168 | if(sasl->resetprefs) {
|
---|
169 | sasl->resetprefs = FALSE;
|
---|
170 | sasl->prefmech = SASL_AUTH_NONE;
|
---|
171 | }
|
---|
172 |
|
---|
173 | if(!strncmp(value, "*", len))
|
---|
174 | sasl->prefmech = SASL_AUTH_DEFAULT;
|
---|
175 | else {
|
---|
176 | unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
|
---|
177 | if(mechbit && mechlen == len)
|
---|
178 | sasl->prefmech |= mechbit;
|
---|
179 | else
|
---|
180 | result = CURLE_URL_MALFORMAT;
|
---|
181 | }
|
---|
182 |
|
---|
183 | return result;
|
---|
184 | }
|
---|
185 |
|
---|
186 | /*
|
---|
187 | * Curl_sasl_init()
|
---|
188 | *
|
---|
189 | * Initializes the SASL structure.
|
---|
190 | */
|
---|
191 | void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
|
---|
192 | const struct SASLproto *params)
|
---|
193 | {
|
---|
194 | unsigned long auth = data->set.httpauth;
|
---|
195 |
|
---|
196 | sasl->params = params; /* Set protocol dependent parameters */
|
---|
197 | sasl->state = SASL_STOP; /* Not yet running */
|
---|
198 | sasl->curmech = NULL; /* No mechanism yet. */
|
---|
199 | sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
|
---|
200 | sasl->prefmech = params->defmechs; /* Default preferred mechanisms */
|
---|
201 | sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */
|
---|
202 | sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */
|
---|
203 | sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */
|
---|
204 | sasl->force_ir = FALSE; /* Respect external option */
|
---|
205 |
|
---|
206 | if(auth != CURLAUTH_BASIC) {
|
---|
207 | sasl->resetprefs = FALSE;
|
---|
208 | sasl->prefmech = SASL_AUTH_NONE;
|
---|
209 | if(auth & CURLAUTH_BASIC)
|
---|
210 | sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
|
---|
211 | if(auth & CURLAUTH_DIGEST)
|
---|
212 | sasl->prefmech |= SASL_MECH_DIGEST_MD5;
|
---|
213 | if(auth & CURLAUTH_NTLM)
|
---|
214 | sasl->prefmech |= SASL_MECH_NTLM;
|
---|
215 | if(auth & CURLAUTH_BEARER)
|
---|
216 | sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
|
---|
217 | if(auth & CURLAUTH_GSSAPI)
|
---|
218 | sasl->prefmech |= SASL_MECH_GSSAPI;
|
---|
219 | }
|
---|
220 | }
|
---|
221 |
|
---|
222 | /*
|
---|
223 | * state()
|
---|
224 | *
|
---|
225 | * This is the ONLY way to change SASL state!
|
---|
226 | */
|
---|
227 | static void state(struct SASL *sasl, struct Curl_easy *data,
|
---|
228 | saslstate newstate)
|
---|
229 | {
|
---|
230 | #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
---|
231 | /* for debug purposes */
|
---|
232 | static const char * const names[]={
|
---|
233 | "STOP",
|
---|
234 | "PLAIN",
|
---|
235 | "LOGIN",
|
---|
236 | "LOGIN_PASSWD",
|
---|
237 | "EXTERNAL",
|
---|
238 | "CRAMMD5",
|
---|
239 | "DIGESTMD5",
|
---|
240 | "DIGESTMD5_RESP",
|
---|
241 | "NTLM",
|
---|
242 | "NTLM_TYPE2MSG",
|
---|
243 | "GSSAPI",
|
---|
244 | "GSSAPI_TOKEN",
|
---|
245 | "GSSAPI_NO_DATA",
|
---|
246 | "OAUTH2",
|
---|
247 | "OAUTH2_RESP",
|
---|
248 | "GSASL",
|
---|
249 | "CANCEL",
|
---|
250 | "FINAL",
|
---|
251 | /* LAST */
|
---|
252 | };
|
---|
253 |
|
---|
254 | if(sasl->state != newstate)
|
---|
255 | infof(data, "SASL %p state change from %s to %s",
|
---|
256 | (void *)sasl, names[sasl->state], names[newstate]);
|
---|
257 | #else
|
---|
258 | (void) data;
|
---|
259 | #endif
|
---|
260 |
|
---|
261 | sasl->state = newstate;
|
---|
262 | }
|
---|
263 |
|
---|
264 | /* Get the SASL server message and convert it to binary. */
|
---|
265 | static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
|
---|
266 | struct bufref *out)
|
---|
267 | {
|
---|
268 | CURLcode result = CURLE_OK;
|
---|
269 |
|
---|
270 | result = sasl->params->getmessage(data, out);
|
---|
271 | if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) {
|
---|
272 | unsigned char *msg;
|
---|
273 | size_t msglen;
|
---|
274 | const char *serverdata = (const char *) Curl_bufref_ptr(out);
|
---|
275 |
|
---|
276 | if(!*serverdata || *serverdata == '=')
|
---|
277 | Curl_bufref_set(out, NULL, 0, NULL);
|
---|
278 | else {
|
---|
279 | result = Curl_base64_decode(serverdata, &msg, &msglen);
|
---|
280 | if(!result)
|
---|
281 | Curl_bufref_set(out, msg, msglen, curl_free);
|
---|
282 | }
|
---|
283 | }
|
---|
284 | return result;
|
---|
285 | }
|
---|
286 |
|
---|
287 | /* Encode the outgoing SASL message. */
|
---|
288 | static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
|
---|
289 | {
|
---|
290 | CURLcode result = CURLE_OK;
|
---|
291 |
|
---|
292 | if(sasl->params->flags & SASL_FLAG_BASE64) {
|
---|
293 | if(!Curl_bufref_ptr(msg)) /* Empty message. */
|
---|
294 | Curl_bufref_set(msg, "", 0, NULL);
|
---|
295 | else if(!Curl_bufref_len(msg)) /* Explicit empty response. */
|
---|
296 | Curl_bufref_set(msg, "=", 1, NULL);
|
---|
297 | else {
|
---|
298 | char *base64;
|
---|
299 | size_t base64len;
|
---|
300 |
|
---|
301 | result = Curl_base64_encode((const char *) Curl_bufref_ptr(msg),
|
---|
302 | Curl_bufref_len(msg), &base64, &base64len);
|
---|
303 | if(!result)
|
---|
304 | Curl_bufref_set(msg, base64, base64len, curl_free);
|
---|
305 | }
|
---|
306 | }
|
---|
307 |
|
---|
308 | return result;
|
---|
309 | }
|
---|
310 |
|
---|
311 | /*
|
---|
312 | * Curl_sasl_can_authenticate()
|
---|
313 | *
|
---|
314 | * Check if we have enough auth data and capabilities to authenticate.
|
---|
315 | */
|
---|
316 | bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
|
---|
317 | {
|
---|
318 | /* Have credentials been provided? */
|
---|
319 | if(data->state.aptr.user)
|
---|
320 | return TRUE;
|
---|
321 |
|
---|
322 | /* EXTERNAL can authenticate without a user name and/or password */
|
---|
323 | if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
|
---|
324 | return TRUE;
|
---|
325 |
|
---|
326 | return FALSE;
|
---|
327 | }
|
---|
328 |
|
---|
329 | /*
|
---|
330 | * Curl_sasl_start()
|
---|
331 | *
|
---|
332 | * Calculate the required login details for SASL authentication.
|
---|
333 | */
|
---|
334 | CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
|
---|
335 | bool force_ir, saslprogress *progress)
|
---|
336 | {
|
---|
337 | CURLcode result = CURLE_OK;
|
---|
338 | struct connectdata *conn = data->conn;
|
---|
339 | unsigned short enabledmechs;
|
---|
340 | const char *mech = NULL;
|
---|
341 | struct bufref resp;
|
---|
342 | saslstate state1 = SASL_STOP;
|
---|
343 | saslstate state2 = SASL_FINAL;
|
---|
344 | const char *hostname, *disp_hostname;
|
---|
345 | int port;
|
---|
346 | #if defined(USE_KERBEROS5) || defined(USE_NTLM)
|
---|
347 | const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
---|
348 | data->set.str[STRING_SERVICE_NAME] :
|
---|
349 | sasl->params->service;
|
---|
350 | #endif
|
---|
351 | const char *oauth_bearer = data->set.str[STRING_BEARER];
|
---|
352 | struct bufref nullmsg;
|
---|
353 |
|
---|
354 | Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
|
---|
355 | Curl_bufref_init(&nullmsg);
|
---|
356 | Curl_bufref_init(&resp);
|
---|
357 | sasl->force_ir = force_ir; /* Latch for future use */
|
---|
358 | sasl->authused = 0; /* No mechanism used yet */
|
---|
359 | enabledmechs = sasl->authmechs & sasl->prefmech;
|
---|
360 | *progress = SASL_IDLE;
|
---|
361 |
|
---|
362 | /* Calculate the supported authentication mechanism, by decreasing order of
|
---|
363 | security, as well as the initial response where appropriate */
|
---|
364 | if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
|
---|
365 | mech = SASL_MECH_STRING_EXTERNAL;
|
---|
366 | state1 = SASL_EXTERNAL;
|
---|
367 | sasl->authused = SASL_MECH_EXTERNAL;
|
---|
368 |
|
---|
369 | if(force_ir || data->set.sasl_ir)
|
---|
370 | result = Curl_auth_create_external_message(conn->user, &resp);
|
---|
371 | }
|
---|
372 | else if(data->state.aptr.user) {
|
---|
373 | #if defined(USE_KERBEROS5)
|
---|
374 | if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
|
---|
375 | Curl_auth_user_contains_domain(conn->user)) {
|
---|
376 | sasl->mutual_auth = FALSE;
|
---|
377 | mech = SASL_MECH_STRING_GSSAPI;
|
---|
378 | state1 = SASL_GSSAPI;
|
---|
379 | state2 = SASL_GSSAPI_TOKEN;
|
---|
380 | sasl->authused = SASL_MECH_GSSAPI;
|
---|
381 |
|
---|
382 | if(force_ir || data->set.sasl_ir)
|
---|
383 | result = Curl_auth_create_gssapi_user_message(data, conn->user,
|
---|
384 | conn->passwd,
|
---|
385 | service,
|
---|
386 | conn->host.name,
|
---|
387 | sasl->mutual_auth,
|
---|
388 | NULL, &conn->krb5,
|
---|
389 | &resp);
|
---|
390 | }
|
---|
391 | else
|
---|
392 | #endif
|
---|
393 | #ifdef USE_GSASL
|
---|
394 | if((enabledmechs & SASL_MECH_SCRAM_SHA_256) &&
|
---|
395 | Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256,
|
---|
396 | &conn->gsasl)) {
|
---|
397 | mech = SASL_MECH_STRING_SCRAM_SHA_256;
|
---|
398 | sasl->authused = SASL_MECH_SCRAM_SHA_256;
|
---|
399 | state1 = SASL_GSASL;
|
---|
400 | state2 = SASL_GSASL;
|
---|
401 |
|
---|
402 | result = Curl_auth_gsasl_start(data, conn->user,
|
---|
403 | conn->passwd, &conn->gsasl);
|
---|
404 | if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
|
---|
405 | result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
|
---|
406 | }
|
---|
407 | else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) &&
|
---|
408 | Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1,
|
---|
409 | &conn->gsasl)) {
|
---|
410 | mech = SASL_MECH_STRING_SCRAM_SHA_1;
|
---|
411 | sasl->authused = SASL_MECH_SCRAM_SHA_1;
|
---|
412 | state1 = SASL_GSASL;
|
---|
413 | state2 = SASL_GSASL;
|
---|
414 |
|
---|
415 | result = Curl_auth_gsasl_start(data, conn->user,
|
---|
416 | conn->passwd, &conn->gsasl);
|
---|
417 | if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
|
---|
418 | result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
|
---|
419 | }
|
---|
420 | else
|
---|
421 | #endif
|
---|
422 | #ifndef CURL_DISABLE_CRYPTO_AUTH
|
---|
423 | if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
|
---|
424 | Curl_auth_is_digest_supported()) {
|
---|
425 | mech = SASL_MECH_STRING_DIGEST_MD5;
|
---|
426 | state1 = SASL_DIGESTMD5;
|
---|
427 | sasl->authused = SASL_MECH_DIGEST_MD5;
|
---|
428 | }
|
---|
429 | else if(enabledmechs & SASL_MECH_CRAM_MD5) {
|
---|
430 | mech = SASL_MECH_STRING_CRAM_MD5;
|
---|
431 | state1 = SASL_CRAMMD5;
|
---|
432 | sasl->authused = SASL_MECH_CRAM_MD5;
|
---|
433 | }
|
---|
434 | else
|
---|
435 | #endif
|
---|
436 | #ifdef USE_NTLM
|
---|
437 | if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
|
---|
438 | mech = SASL_MECH_STRING_NTLM;
|
---|
439 | state1 = SASL_NTLM;
|
---|
440 | state2 = SASL_NTLM_TYPE2MSG;
|
---|
441 | sasl->authused = SASL_MECH_NTLM;
|
---|
442 |
|
---|
443 | if(force_ir || data->set.sasl_ir)
|
---|
444 | result = Curl_auth_create_ntlm_type1_message(data,
|
---|
445 | conn->user, conn->passwd,
|
---|
446 | service,
|
---|
447 | hostname,
|
---|
448 | &conn->ntlm, &resp);
|
---|
449 | }
|
---|
450 | else
|
---|
451 | #endif
|
---|
452 | if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) {
|
---|
453 | mech = SASL_MECH_STRING_OAUTHBEARER;
|
---|
454 | state1 = SASL_OAUTH2;
|
---|
455 | state2 = SASL_OAUTH2_RESP;
|
---|
456 | sasl->authused = SASL_MECH_OAUTHBEARER;
|
---|
457 |
|
---|
458 | if(force_ir || data->set.sasl_ir)
|
---|
459 | result = Curl_auth_create_oauth_bearer_message(conn->user,
|
---|
460 | hostname,
|
---|
461 | port,
|
---|
462 | oauth_bearer,
|
---|
463 | &resp);
|
---|
464 | }
|
---|
465 | else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) {
|
---|
466 | mech = SASL_MECH_STRING_XOAUTH2;
|
---|
467 | state1 = SASL_OAUTH2;
|
---|
468 | sasl->authused = SASL_MECH_XOAUTH2;
|
---|
469 |
|
---|
470 | if(force_ir || data->set.sasl_ir)
|
---|
471 | result = Curl_auth_create_xoauth_bearer_message(conn->user,
|
---|
472 | oauth_bearer,
|
---|
473 | &resp);
|
---|
474 | }
|
---|
475 | else if(enabledmechs & SASL_MECH_PLAIN) {
|
---|
476 | mech = SASL_MECH_STRING_PLAIN;
|
---|
477 | state1 = SASL_PLAIN;
|
---|
478 | sasl->authused = SASL_MECH_PLAIN;
|
---|
479 |
|
---|
480 | if(force_ir || data->set.sasl_ir)
|
---|
481 | result = Curl_auth_create_plain_message(conn->sasl_authzid,
|
---|
482 | conn->user, conn->passwd,
|
---|
483 | &resp);
|
---|
484 | }
|
---|
485 | else if(enabledmechs & SASL_MECH_LOGIN) {
|
---|
486 | mech = SASL_MECH_STRING_LOGIN;
|
---|
487 | state1 = SASL_LOGIN;
|
---|
488 | state2 = SASL_LOGIN_PASSWD;
|
---|
489 | sasl->authused = SASL_MECH_LOGIN;
|
---|
490 |
|
---|
491 | if(force_ir || data->set.sasl_ir)
|
---|
492 | result = Curl_auth_create_login_message(conn->user, &resp);
|
---|
493 | }
|
---|
494 | }
|
---|
495 |
|
---|
496 | if(!result && mech) {
|
---|
497 | sasl->curmech = mech;
|
---|
498 | if(Curl_bufref_ptr(&resp))
|
---|
499 | result = build_message(sasl, &resp);
|
---|
500 |
|
---|
501 | if(sasl->params->maxirlen &&
|
---|
502 | strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen)
|
---|
503 | Curl_bufref_free(&resp);
|
---|
504 |
|
---|
505 | if(!result)
|
---|
506 | result = sasl->params->sendauth(data, mech, &resp);
|
---|
507 |
|
---|
508 | if(!result) {
|
---|
509 | *progress = SASL_INPROGRESS;
|
---|
510 | state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
|
---|
511 | }
|
---|
512 | }
|
---|
513 |
|
---|
514 | Curl_bufref_free(&resp);
|
---|
515 | return result;
|
---|
516 | }
|
---|
517 |
|
---|
518 | /*
|
---|
519 | * Curl_sasl_continue()
|
---|
520 | *
|
---|
521 | * Continue the authentication.
|
---|
522 | */
|
---|
523 | CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
---|
524 | int code, saslprogress *progress)
|
---|
525 | {
|
---|
526 | CURLcode result = CURLE_OK;
|
---|
527 | struct connectdata *conn = data->conn;
|
---|
528 | saslstate newstate = SASL_FINAL;
|
---|
529 | struct bufref resp;
|
---|
530 | const char *hostname, *disp_hostname;
|
---|
531 | int port;
|
---|
532 | #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \
|
---|
533 | defined(USE_NTLM)
|
---|
534 | const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
---|
535 | data->set.str[STRING_SERVICE_NAME] :
|
---|
536 | sasl->params->service;
|
---|
537 | #endif
|
---|
538 | const char *oauth_bearer = data->set.str[STRING_BEARER];
|
---|
539 | struct bufref serverdata;
|
---|
540 |
|
---|
541 | Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
|
---|
542 | Curl_bufref_init(&serverdata);
|
---|
543 | Curl_bufref_init(&resp);
|
---|
544 | *progress = SASL_INPROGRESS;
|
---|
545 |
|
---|
546 | if(sasl->state == SASL_FINAL) {
|
---|
547 | if(code != sasl->params->finalcode)
|
---|
548 | result = CURLE_LOGIN_DENIED;
|
---|
549 | *progress = SASL_DONE;
|
---|
550 | state(sasl, data, SASL_STOP);
|
---|
551 | return result;
|
---|
552 | }
|
---|
553 |
|
---|
554 | if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
|
---|
555 | code != sasl->params->contcode) {
|
---|
556 | *progress = SASL_DONE;
|
---|
557 | state(sasl, data, SASL_STOP);
|
---|
558 | return CURLE_LOGIN_DENIED;
|
---|
559 | }
|
---|
560 |
|
---|
561 | switch(sasl->state) {
|
---|
562 | case SASL_STOP:
|
---|
563 | *progress = SASL_DONE;
|
---|
564 | return result;
|
---|
565 | case SASL_PLAIN:
|
---|
566 | result = Curl_auth_create_plain_message(conn->sasl_authzid,
|
---|
567 | conn->user, conn->passwd, &resp);
|
---|
568 | break;
|
---|
569 | case SASL_LOGIN:
|
---|
570 | result = Curl_auth_create_login_message(conn->user, &resp);
|
---|
571 | newstate = SASL_LOGIN_PASSWD;
|
---|
572 | break;
|
---|
573 | case SASL_LOGIN_PASSWD:
|
---|
574 | result = Curl_auth_create_login_message(conn->passwd, &resp);
|
---|
575 | break;
|
---|
576 | case SASL_EXTERNAL:
|
---|
577 | result = Curl_auth_create_external_message(conn->user, &resp);
|
---|
578 | break;
|
---|
579 | #ifndef CURL_DISABLE_CRYPTO_AUTH
|
---|
580 | #ifdef USE_GSASL
|
---|
581 | case SASL_GSASL:
|
---|
582 | result = get_server_message(sasl, data, &serverdata);
|
---|
583 | if(!result)
|
---|
584 | result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp);
|
---|
585 | if(!result && Curl_bufref_len(&resp) > 0)
|
---|
586 | newstate = SASL_GSASL;
|
---|
587 | break;
|
---|
588 | #endif
|
---|
589 | case SASL_CRAMMD5:
|
---|
590 | result = get_server_message(sasl, data, &serverdata);
|
---|
591 | if(!result)
|
---|
592 | result = Curl_auth_create_cram_md5_message(&serverdata, conn->user,
|
---|
593 | conn->passwd, &resp);
|
---|
594 | break;
|
---|
595 | case SASL_DIGESTMD5:
|
---|
596 | result = get_server_message(sasl, data, &serverdata);
|
---|
597 | if(!result)
|
---|
598 | result = Curl_auth_create_digest_md5_message(data, &serverdata,
|
---|
599 | conn->user, conn->passwd,
|
---|
600 | service, &resp);
|
---|
601 | if(!result && (sasl->params->flags & SASL_FLAG_BASE64))
|
---|
602 | newstate = SASL_DIGESTMD5_RESP;
|
---|
603 | break;
|
---|
604 | case SASL_DIGESTMD5_RESP:
|
---|
605 | /* Keep response NULL to output an empty line. */
|
---|
606 | break;
|
---|
607 | #endif
|
---|
608 |
|
---|
609 | #ifdef USE_NTLM
|
---|
610 | case SASL_NTLM:
|
---|
611 | /* Create the type-1 message */
|
---|
612 | result = Curl_auth_create_ntlm_type1_message(data,
|
---|
613 | conn->user, conn->passwd,
|
---|
614 | service, hostname,
|
---|
615 | &conn->ntlm, &resp);
|
---|
616 | newstate = SASL_NTLM_TYPE2MSG;
|
---|
617 | break;
|
---|
618 | case SASL_NTLM_TYPE2MSG:
|
---|
619 | /* Decode the type-2 message */
|
---|
620 | result = get_server_message(sasl, data, &serverdata);
|
---|
621 | if(!result)
|
---|
622 | result = Curl_auth_decode_ntlm_type2_message(data, &serverdata,
|
---|
623 | &conn->ntlm);
|
---|
624 | if(!result)
|
---|
625 | result = Curl_auth_create_ntlm_type3_message(data, conn->user,
|
---|
626 | conn->passwd, &conn->ntlm,
|
---|
627 | &resp);
|
---|
628 | break;
|
---|
629 | #endif
|
---|
630 |
|
---|
631 | #if defined(USE_KERBEROS5)
|
---|
632 | case SASL_GSSAPI:
|
---|
633 | result = Curl_auth_create_gssapi_user_message(data, conn->user,
|
---|
634 | conn->passwd,
|
---|
635 | service,
|
---|
636 | conn->host.name,
|
---|
637 | sasl->mutual_auth, NULL,
|
---|
638 | &conn->krb5,
|
---|
639 | &resp);
|
---|
640 | newstate = SASL_GSSAPI_TOKEN;
|
---|
641 | break;
|
---|
642 | case SASL_GSSAPI_TOKEN:
|
---|
643 | result = get_server_message(sasl, data, &serverdata);
|
---|
644 | if(!result) {
|
---|
645 | if(sasl->mutual_auth) {
|
---|
646 | /* Decode the user token challenge and create the optional response
|
---|
647 | message */
|
---|
648 | result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
|
---|
649 | NULL, NULL,
|
---|
650 | sasl->mutual_auth,
|
---|
651 | &serverdata,
|
---|
652 | &conn->krb5,
|
---|
653 | &resp);
|
---|
654 | newstate = SASL_GSSAPI_NO_DATA;
|
---|
655 | }
|
---|
656 | else
|
---|
657 | /* Decode the security challenge and create the response message */
|
---|
658 | result = Curl_auth_create_gssapi_security_message(data,
|
---|
659 | conn->sasl_authzid,
|
---|
660 | &serverdata,
|
---|
661 | &conn->krb5,
|
---|
662 | &resp);
|
---|
663 | }
|
---|
664 | break;
|
---|
665 | case SASL_GSSAPI_NO_DATA:
|
---|
666 | /* Decode the security challenge and create the response message */
|
---|
667 | result = get_server_message(sasl, data, &serverdata);
|
---|
668 | if(!result)
|
---|
669 | result = Curl_auth_create_gssapi_security_message(data,
|
---|
670 | conn->sasl_authzid,
|
---|
671 | &serverdata,
|
---|
672 | &conn->krb5,
|
---|
673 | &resp);
|
---|
674 | break;
|
---|
675 | #endif
|
---|
676 |
|
---|
677 | case SASL_OAUTH2:
|
---|
678 | /* Create the authorization message */
|
---|
679 | if(sasl->authused == SASL_MECH_OAUTHBEARER) {
|
---|
680 | result = Curl_auth_create_oauth_bearer_message(conn->user,
|
---|
681 | hostname,
|
---|
682 | port,
|
---|
683 | oauth_bearer,
|
---|
684 | &resp);
|
---|
685 |
|
---|
686 | /* Failures maybe sent by the server as continuations for OAUTHBEARER */
|
---|
687 | newstate = SASL_OAUTH2_RESP;
|
---|
688 | }
|
---|
689 | else
|
---|
690 | result = Curl_auth_create_xoauth_bearer_message(conn->user,
|
---|
691 | oauth_bearer,
|
---|
692 | &resp);
|
---|
693 | break;
|
---|
694 |
|
---|
695 | case SASL_OAUTH2_RESP:
|
---|
696 | /* The continuation is optional so check the response code */
|
---|
697 | if(code == sasl->params->finalcode) {
|
---|
698 | /* Final response was received so we are done */
|
---|
699 | *progress = SASL_DONE;
|
---|
700 | state(sasl, data, SASL_STOP);
|
---|
701 | return result;
|
---|
702 | }
|
---|
703 | else if(code == sasl->params->contcode) {
|
---|
704 | /* Acknowledge the continuation by sending a 0x01 response. */
|
---|
705 | Curl_bufref_set(&resp, "\x01", 1, NULL);
|
---|
706 | break;
|
---|
707 | }
|
---|
708 | else {
|
---|
709 | *progress = SASL_DONE;
|
---|
710 | state(sasl, data, SASL_STOP);
|
---|
711 | return CURLE_LOGIN_DENIED;
|
---|
712 | }
|
---|
713 |
|
---|
714 | case SASL_CANCEL:
|
---|
715 | /* Remove the offending mechanism from the supported list */
|
---|
716 | sasl->authmechs ^= sasl->authused;
|
---|
717 |
|
---|
718 | /* Start an alternative SASL authentication */
|
---|
719 | return Curl_sasl_start(sasl, data, sasl->force_ir, progress);
|
---|
720 | default:
|
---|
721 | failf(data, "Unsupported SASL authentication mechanism");
|
---|
722 | result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
|
---|
723 | break;
|
---|
724 | }
|
---|
725 |
|
---|
726 | Curl_bufref_free(&serverdata);
|
---|
727 |
|
---|
728 | switch(result) {
|
---|
729 | case CURLE_BAD_CONTENT_ENCODING:
|
---|
730 | /* Cancel dialog */
|
---|
731 | result = sasl->params->cancelauth(data, sasl->curmech);
|
---|
732 | newstate = SASL_CANCEL;
|
---|
733 | break;
|
---|
734 | case CURLE_OK:
|
---|
735 | result = build_message(sasl, &resp);
|
---|
736 | if(!result)
|
---|
737 | result = sasl->params->contauth(data, sasl->curmech, &resp);
|
---|
738 | break;
|
---|
739 | default:
|
---|
740 | newstate = SASL_STOP; /* Stop on error */
|
---|
741 | *progress = SASL_DONE;
|
---|
742 | break;
|
---|
743 | }
|
---|
744 |
|
---|
745 | Curl_bufref_free(&resp);
|
---|
746 |
|
---|
747 | state(sasl, data, newstate);
|
---|
748 |
|
---|
749 | return result;
|
---|
750 | }
|
---|
751 | #endif /* protocols are enabled that use SASL */
|
---|