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