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