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