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