quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

curl_sasl.c (30297B)


      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 "curlx/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 "curlx/warnless.h"
     53 #include "sendf.h"
     54 /* The last 3 #include files should be in this order */
     55 #include "curl_printf.h"
     56 #include "curl_memory.h"
     57 #include "memdebug.h"
     58 
     59 /* Supported mechanisms */
     60 static const struct {
     61   const char    *name;  /* Name */
     62   size_t         len;   /* Name length */
     63   unsigned short bit;   /* Flag bit */
     64 } mechtable[] = {
     65   { "LOGIN",        5,  SASL_MECH_LOGIN },
     66   { "PLAIN",        5,  SASL_MECH_PLAIN },
     67   { "CRAM-MD5",     8,  SASL_MECH_CRAM_MD5 },
     68   { "DIGEST-MD5",   10, SASL_MECH_DIGEST_MD5 },
     69   { "GSSAPI",       6,  SASL_MECH_GSSAPI },
     70   { "EXTERNAL",     8,  SASL_MECH_EXTERNAL },
     71   { "NTLM",         4,  SASL_MECH_NTLM },
     72   { "XOAUTH2",      7,  SASL_MECH_XOAUTH2 },
     73   { "OAUTHBEARER",  11, SASL_MECH_OAUTHBEARER },
     74   { "SCRAM-SHA-1",  11, SASL_MECH_SCRAM_SHA_1 },
     75   { "SCRAM-SHA-256",13, SASL_MECH_SCRAM_SHA_256 },
     76   { ZERO_NULL,      0,  0 }
     77 };
     78 
     79 /*
     80  * Curl_sasl_decode_mech()
     81  *
     82  * Convert a SASL mechanism name into a token.
     83  *
     84  * Parameters:
     85  *
     86  * ptr    [in]     - The mechanism string.
     87  * maxlen [in]     - Maximum mechanism string length.
     88  * len    [out]    - If not NULL, effective name length.
     89  *
     90  * Returns the SASL mechanism token or 0 if no match.
     91  */
     92 unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen,
     93                                      size_t *len)
     94 {
     95   unsigned int i;
     96   char c;
     97 
     98   for(i = 0; mechtable[i].name; i++) {
     99     if(maxlen >= mechtable[i].len &&
    100        !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
    101       if(len)
    102         *len = mechtable[i].len;
    103 
    104       if(maxlen == mechtable[i].len)
    105         return mechtable[i].bit;
    106 
    107       c = ptr[mechtable[i].len];
    108       if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
    109         return mechtable[i].bit;
    110     }
    111   }
    112 
    113   return 0;
    114 }
    115 
    116 /*
    117  * Curl_sasl_parse_url_auth_option()
    118  *
    119  * Parse the URL login options.
    120  */
    121 CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
    122                                          const char *value, size_t len)
    123 {
    124   CURLcode result = CURLE_OK;
    125   size_t mechlen;
    126 
    127   if(!len)
    128     return CURLE_URL_MALFORMAT;
    129 
    130   if(sasl->resetprefs) {
    131     sasl->resetprefs = FALSE;
    132     sasl->prefmech = SASL_AUTH_NONE;
    133   }
    134 
    135   if(!strncmp(value, "*", len))
    136     sasl->prefmech = SASL_AUTH_DEFAULT;
    137   else {
    138     unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
    139     if(mechbit && mechlen == len)
    140       sasl->prefmech |= mechbit;
    141     else
    142       result = CURLE_URL_MALFORMAT;
    143   }
    144 
    145   return result;
    146 }
    147 
    148 /*
    149  * Curl_sasl_init()
    150  *
    151  * Initializes the SASL structure.
    152  */
    153 void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
    154                     const struct SASLproto *params)
    155 {
    156   unsigned long auth = data->set.httpauth;
    157 
    158   sasl->params = params;           /* Set protocol dependent parameters */
    159   sasl->state = SASL_STOP;         /* Not yet running */
    160   sasl->curmech = NULL;            /* No mechanism yet. */
    161   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
    162   sasl->prefmech = params->defmechs; /* Default preferred mechanisms */
    163   sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */
    164   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
    165   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
    166   sasl->force_ir = FALSE;          /* Respect external option */
    167 
    168   if(auth != CURLAUTH_BASIC) {
    169     unsigned short mechs = SASL_AUTH_NONE;
    170 
    171     /* If some usable http authentication options have been set, determine
    172        new defaults from them. */
    173     if(auth & CURLAUTH_BASIC)
    174       mechs |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
    175     if(auth & CURLAUTH_DIGEST)
    176       mechs |= SASL_MECH_DIGEST_MD5;
    177     if(auth & CURLAUTH_NTLM)
    178       mechs |= SASL_MECH_NTLM;
    179     if(auth & CURLAUTH_BEARER)
    180       mechs |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
    181     if(auth & CURLAUTH_GSSAPI)
    182       mechs |= SASL_MECH_GSSAPI;
    183 
    184     if(mechs != SASL_AUTH_NONE)
    185       sasl->prefmech = mechs;
    186   }
    187 }
    188 
    189 /*
    190  * sasl_state()
    191  *
    192  * This is the ONLY way to change SASL state!
    193  */
    194 static void sasl_state(struct SASL *sasl, struct Curl_easy *data,
    195                        saslstate newstate)
    196 {
    197 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
    198   /* for debug purposes */
    199   static const char * const names[]={
    200     "STOP",
    201     "PLAIN",
    202     "LOGIN",
    203     "LOGIN_PASSWD",
    204     "EXTERNAL",
    205     "CRAMMD5",
    206     "DIGESTMD5",
    207     "DIGESTMD5_RESP",
    208     "NTLM",
    209     "NTLM_TYPE2MSG",
    210     "GSSAPI",
    211     "GSSAPI_TOKEN",
    212     "GSSAPI_NO_DATA",
    213     "OAUTH2",
    214     "OAUTH2_RESP",
    215     "GSASL",
    216     "CANCEL",
    217     "FINAL",
    218     /* LAST */
    219   };
    220 
    221   if(sasl->state != newstate)
    222     infof(data, "SASL %p state change from %s to %s",
    223           (void *)sasl, names[sasl->state], names[newstate]);
    224 #else
    225   (void) data;
    226 #endif
    227 
    228   sasl->state = newstate;
    229 }
    230 
    231 #if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \
    232   !defined(CURL_DISABLE_DIGEST_AUTH)
    233 /* Get the SASL server message and convert it to binary. */
    234 static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
    235                                    struct bufref *out)
    236 {
    237   CURLcode result = CURLE_OK;
    238 
    239   result = sasl->params->getmessage(data, out);
    240   if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) {
    241     unsigned char *msg;
    242     size_t msglen;
    243     const char *serverdata = (const char *) Curl_bufref_ptr(out);
    244 
    245     if(!*serverdata || *serverdata == '=')
    246       Curl_bufref_set(out, NULL, 0, NULL);
    247     else {
    248       result = curlx_base64_decode(serverdata, &msg, &msglen);
    249       if(!result)
    250         Curl_bufref_set(out, msg, msglen, curl_free);
    251     }
    252   }
    253   return result;
    254 }
    255 #endif
    256 
    257 /* Encode the outgoing SASL message. */
    258 static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
    259 {
    260   CURLcode result = CURLE_OK;
    261 
    262   if(sasl->params->flags & SASL_FLAG_BASE64) {
    263     if(!Curl_bufref_ptr(msg))                   /* Empty message. */
    264       Curl_bufref_set(msg, "", 0, NULL);
    265     else if(!Curl_bufref_len(msg))              /* Explicit empty response. */
    266       Curl_bufref_set(msg, "=", 1, NULL);
    267     else {
    268       char *base64;
    269       size_t base64len;
    270 
    271       result = curlx_base64_encode((const char *) Curl_bufref_ptr(msg),
    272                                    Curl_bufref_len(msg), &base64, &base64len);
    273       if(!result)
    274         Curl_bufref_set(msg, base64, base64len, curl_free);
    275     }
    276   }
    277 
    278   return result;
    279 }
    280 
    281 /*
    282  * Curl_sasl_can_authenticate()
    283  *
    284  * Check if we have enough auth data and capabilities to authenticate.
    285  */
    286 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
    287 {
    288   /* Have credentials been provided? */
    289   if(data->state.aptr.user)
    290     return TRUE;
    291 
    292   /* EXTERNAL can authenticate without a username and/or password */
    293   if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
    294     return TRUE;
    295 
    296   return FALSE;
    297 }
    298 
    299 struct sasl_ctx {
    300   struct SASL *sasl;
    301   struct connectdata *conn;
    302   const char *user;
    303   unsigned short enabledmechs;
    304   const char *mech;
    305   saslstate state1;
    306   saslstate state2;
    307   struct bufref resp;
    308   CURLcode result;
    309 };
    310 
    311 static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx)
    312 {
    313   if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) {
    314     sctx->mech = SASL_MECH_STRING_EXTERNAL;
    315     sctx->state1 = SASL_EXTERNAL;
    316     sctx->sasl->authused = SASL_MECH_EXTERNAL;
    317 
    318     if(sctx->sasl->force_ir || data->set.sasl_ir)
    319       Curl_auth_create_external_message(sctx->conn->user, &sctx->resp);
    320     return TRUE;
    321   }
    322   return FALSE;
    323 }
    324 
    325 #ifdef USE_KERBEROS5
    326 static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx)
    327 {
    328   if(sctx->user &&
    329      (sctx->enabledmechs & SASL_MECH_GSSAPI) &&
    330      Curl_auth_is_gssapi_supported() &&
    331      Curl_auth_user_contains_domain(sctx->conn->user)) {
    332     const char *service = data->set.str[STRING_SERVICE_NAME] ?
    333       data->set.str[STRING_SERVICE_NAME] :
    334       sctx->sasl->params->service;
    335 
    336     sctx->sasl->mutual_auth = FALSE;
    337     sctx->mech = SASL_MECH_STRING_GSSAPI;
    338     sctx->state1 = SASL_GSSAPI;
    339     sctx->state2 = SASL_GSSAPI_TOKEN;
    340     sctx->sasl->authused = SASL_MECH_GSSAPI;
    341 
    342     if(sctx->sasl->force_ir || data->set.sasl_ir) {
    343       struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn);
    344       sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY :
    345         Curl_auth_create_gssapi_user_message(data, sctx->conn->user,
    346                                              sctx->conn->passwd,
    347                                              service, sctx->conn->host.name,
    348                                              sctx->sasl->mutual_auth, NULL,
    349                                              krb5, &sctx->resp);
    350     }
    351     return TRUE;
    352   }
    353   return FALSE;
    354 }
    355 #endif /* USE_KERBEROS5 */
    356 
    357 #ifdef USE_GSASL
    358 static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx)
    359 {
    360   struct gsasldata *gsasl;
    361   struct bufref nullmsg;
    362 
    363   if(sctx->user &&
    364      (sctx->enabledmechs & (SASL_MECH_SCRAM_SHA_256|SASL_MECH_SCRAM_SHA_1))) {
    365     gsasl = Curl_auth_gsasl_get(sctx->conn);
    366     if(!gsasl) {
    367       sctx->result = CURLE_OUT_OF_MEMORY;
    368       return TRUE; /* attempted, but failed */
    369     }
    370 
    371     if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_256) &&
    372       Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256,
    373                                    gsasl)) {
    374       sctx->mech = SASL_MECH_STRING_SCRAM_SHA_256;
    375       sctx->sasl->authused = SASL_MECH_SCRAM_SHA_256;
    376     }
    377     else if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_1) &&
    378       Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1,
    379                                    gsasl)) {
    380       sctx->mech = SASL_MECH_STRING_SCRAM_SHA_1;
    381       sctx->sasl->authused = SASL_MECH_SCRAM_SHA_1;
    382     }
    383     else
    384       return FALSE;
    385 
    386     Curl_bufref_init(&nullmsg);
    387     sctx->state1 = SASL_GSASL;
    388     sctx->state2 = SASL_GSASL;
    389     sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user,
    390                                          sctx->conn->passwd, gsasl);
    391     if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir))
    392       sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp);
    393     return TRUE;
    394   }
    395   return FALSE;
    396 }
    397 
    398 #endif /* USE_GSASL */
    399 
    400 #ifndef CURL_DISABLE_DIGEST_AUTH
    401 static bool sasl_choose_digest(struct Curl_easy *data, struct sasl_ctx *sctx)
    402 {
    403   (void)data;
    404   if(!sctx->user)
    405     return FALSE;
    406   else if((sctx->enabledmechs & SASL_MECH_DIGEST_MD5) &&
    407      Curl_auth_is_digest_supported()) {
    408     sctx->mech = SASL_MECH_STRING_DIGEST_MD5;
    409     sctx->state1 = SASL_DIGESTMD5;
    410     sctx->sasl->authused = SASL_MECH_DIGEST_MD5;
    411     return TRUE;
    412   }
    413   else if(sctx->enabledmechs & SASL_MECH_CRAM_MD5) {
    414     sctx->mech = SASL_MECH_STRING_CRAM_MD5;
    415     sctx->state1 = SASL_CRAMMD5;
    416     sctx->sasl->authused = SASL_MECH_CRAM_MD5;
    417     return TRUE;
    418   }
    419   return FALSE;
    420 }
    421 #endif /* !CURL_DISABLE_DIGEST_AUTH */
    422 
    423 #ifdef USE_NTLM
    424 static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx)
    425 {
    426   if(!sctx->user)
    427     return FALSE;
    428   else if((sctx->enabledmechs & SASL_MECH_NTLM) &&
    429           Curl_auth_is_ntlm_supported()) {
    430     const char *service = data->set.str[STRING_SERVICE_NAME] ?
    431       data->set.str[STRING_SERVICE_NAME] :
    432       sctx->sasl->params->service;
    433     const char *hostname;
    434     int port;
    435 
    436     Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
    437 
    438     sctx->mech = SASL_MECH_STRING_NTLM;
    439     sctx->state1 = SASL_NTLM;
    440     sctx->state2 = SASL_NTLM_TYPE2MSG;
    441     sctx->sasl->authused = SASL_MECH_NTLM;
    442 
    443     if(sctx->sasl->force_ir || data->set.sasl_ir) {
    444       struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE);
    445       sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY :
    446         Curl_auth_create_ntlm_type1_message(data,
    447                                             sctx->conn->user,
    448                                             sctx->conn->passwd,
    449                                             service, hostname,
    450                                             ntlm, &sctx->resp);
    451     }
    452     return TRUE;
    453   }
    454   return FALSE;
    455 }
    456 #endif /* USE_NTLM */
    457 
    458 static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx)
    459 {
    460   const char *oauth_bearer = data->set.str[STRING_BEARER];
    461 
    462   if(sctx->user && oauth_bearer &&
    463      (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) {
    464     const char *hostname;
    465     int port;
    466     Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
    467 
    468     sctx->mech = SASL_MECH_STRING_OAUTHBEARER;
    469     sctx->state1 = SASL_OAUTH2;
    470     sctx->state2 = SASL_OAUTH2_RESP;
    471     sctx->sasl->authused = SASL_MECH_OAUTHBEARER;
    472 
    473     if(sctx->sasl->force_ir || data->set.sasl_ir)
    474       sctx->result =
    475         Curl_auth_create_oauth_bearer_message(sctx->conn->user,
    476                                               hostname, port,
    477                                               oauth_bearer, &sctx->resp);
    478     return TRUE;
    479   }
    480   return FALSE;
    481 }
    482 
    483 static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx)
    484 {
    485   const char *oauth_bearer = data->set.str[STRING_BEARER];
    486 
    487   if(sctx->user && oauth_bearer &&
    488      (sctx->enabledmechs & SASL_MECH_XOAUTH2)) {
    489     sctx->mech = SASL_MECH_STRING_XOAUTH2;
    490     sctx->state1 = SASL_OAUTH2;
    491     sctx->sasl->authused = SASL_MECH_XOAUTH2;
    492 
    493     if(sctx->sasl->force_ir || data->set.sasl_ir)
    494       sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user,
    495                                                       oauth_bearer,
    496                                                       &sctx->resp);
    497     return TRUE;
    498   }
    499   return FALSE;
    500 }
    501 
    502 static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx)
    503 {
    504   if(sctx->user && (sctx->enabledmechs & SASL_MECH_PLAIN)) {
    505     sctx->mech = SASL_MECH_STRING_PLAIN;
    506     sctx->state1 = SASL_PLAIN;
    507     sctx->sasl->authused = SASL_MECH_PLAIN;
    508 
    509     if(sctx->sasl->force_ir || data->set.sasl_ir)
    510       sctx->result =
    511         Curl_auth_create_plain_message(sctx->conn->sasl_authzid,
    512                                        sctx->conn->user, sctx->conn->passwd,
    513                                        &sctx->resp);
    514     return TRUE;
    515   }
    516   return FALSE;
    517 }
    518 
    519 static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx)
    520 {
    521   if(sctx->user && (sctx->enabledmechs & SASL_MECH_LOGIN)) {
    522     sctx->mech = SASL_MECH_STRING_LOGIN;
    523     sctx->state1 = SASL_LOGIN;
    524     sctx->state2 = SASL_LOGIN_PASSWD;
    525     sctx->sasl->authused = SASL_MECH_LOGIN;
    526 
    527     if(sctx->sasl->force_ir || data->set.sasl_ir)
    528       Curl_auth_create_login_message(sctx->conn->user, &sctx->resp);
    529     return TRUE;
    530   }
    531   return FALSE;
    532 }
    533 
    534 /*
    535  * Curl_sasl_start()
    536  *
    537  * Calculate the required login details for SASL authentication.
    538  */
    539 CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
    540                          bool force_ir, saslprogress *progress)
    541 {
    542   struct sasl_ctx sctx;
    543 
    544   sasl->force_ir = force_ir;    /* Latch for future use */
    545   sasl->authused = 0;           /* No mechanism used yet */
    546   *progress = SASL_IDLE;
    547 
    548   memset(&sctx, 0, sizeof(sctx));
    549   sctx.sasl = sasl;
    550   sctx.conn = data->conn;
    551   sctx.user = data->state.aptr.user;
    552   Curl_bufref_init(&sctx.resp);
    553   sctx.enabledmechs = sasl->authmechs & sasl->prefmech;
    554   sctx.state1 = SASL_STOP;
    555   sctx.state2 = SASL_FINAL;
    556 
    557   /* Calculate the supported authentication mechanism, by decreasing order of
    558      security, as well as the initial response where appropriate */
    559   if(sasl_choose_external(data, &sctx) ||
    560 #if defined(USE_KERBEROS5)
    561      sasl_choose_krb5(data, &sctx) ||
    562 #endif
    563 #ifdef USE_GSASL
    564      sasl_choose_gsasl(data, &sctx) ||
    565 #endif
    566 #ifndef CURL_DISABLE_DIGEST_AUTH
    567      sasl_choose_digest(data, &sctx) ||
    568 #endif
    569 #ifdef USE_NTLM
    570      sasl_choose_ntlm(data, &sctx) ||
    571 #endif
    572      sasl_choose_oauth(data, &sctx) ||
    573      sasl_choose_oauth2(data, &sctx) ||
    574      sasl_choose_plain(data, &sctx) ||
    575      sasl_choose_login(data, &sctx)) {
    576     /* selected, either we have a mechanism or a failure */
    577     DEBUGASSERT(sctx.mech || sctx.result);
    578   }
    579 
    580   if(!sctx.result && sctx.mech) {
    581     sasl->curmech = sctx.mech;
    582     if(Curl_bufref_ptr(&sctx.resp))
    583       sctx.result = build_message(sasl, &sctx.resp);
    584 
    585     if(sasl->params->maxirlen &&
    586        strlen(sctx.mech) + Curl_bufref_len(&sctx.resp) >
    587          sasl->params->maxirlen)
    588       Curl_bufref_free(&sctx.resp);
    589 
    590     if(!sctx.result)
    591       sctx.result = sasl->params->sendauth(data, sctx.mech, &sctx.resp);
    592 
    593     if(!sctx.result) {
    594       *progress = SASL_INPROGRESS;
    595       sasl_state(sasl, data, Curl_bufref_ptr(&sctx.resp) ?
    596                  sctx.state2 : sctx.state1);
    597     }
    598   }
    599 
    600   Curl_bufref_free(&sctx.resp);
    601   return sctx.result;
    602 }
    603 
    604 /*
    605  * Curl_sasl_continue()
    606  *
    607  * Continue the authentication.
    608  */
    609 CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
    610                             int code, saslprogress *progress)
    611 {
    612   CURLcode result = CURLE_OK;
    613   struct connectdata *conn = data->conn;
    614   saslstate newstate = SASL_FINAL;
    615   struct bufref resp;
    616   const char *hostname;
    617   int port;
    618 #if defined(USE_KERBEROS5) || defined(USE_NTLM) \
    619     || !defined(CURL_DISABLE_DIGEST_AUTH)
    620   const char *service = data->set.str[STRING_SERVICE_NAME] ?
    621     data->set.str[STRING_SERVICE_NAME] :
    622     sasl->params->service;
    623 #endif
    624   const char *oauth_bearer = data->set.str[STRING_BEARER];
    625   struct bufref serverdata;
    626 
    627   Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
    628   Curl_bufref_init(&serverdata);
    629   Curl_bufref_init(&resp);
    630   *progress = SASL_INPROGRESS;
    631 
    632   if(sasl->state == SASL_FINAL) {
    633     if(code != sasl->params->finalcode)
    634       result = CURLE_LOGIN_DENIED;
    635     *progress = SASL_DONE;
    636     sasl_state(sasl, data, SASL_STOP);
    637     return result;
    638   }
    639 
    640   if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
    641      code != sasl->params->contcode) {
    642     *progress = SASL_DONE;
    643     sasl_state(sasl, data, SASL_STOP);
    644     return CURLE_LOGIN_DENIED;
    645   }
    646 
    647   switch(sasl->state) {
    648   case SASL_STOP:
    649     *progress = SASL_DONE;
    650     return result;
    651   case SASL_PLAIN:
    652     result = Curl_auth_create_plain_message(conn->sasl_authzid,
    653                                             conn->user, conn->passwd, &resp);
    654     break;
    655   case SASL_LOGIN:
    656     Curl_auth_create_login_message(conn->user, &resp);
    657     newstate = SASL_LOGIN_PASSWD;
    658     break;
    659   case SASL_LOGIN_PASSWD:
    660     Curl_auth_create_login_message(conn->passwd, &resp);
    661     break;
    662   case SASL_EXTERNAL:
    663     Curl_auth_create_external_message(conn->user, &resp);
    664     break;
    665 #ifdef USE_GSASL
    666   case SASL_GSASL:
    667     result = get_server_message(sasl, data, &serverdata);
    668     if(!result) {
    669       struct gsasldata *gsasl = Curl_auth_gsasl_get(conn);
    670       result = !gsasl ? CURLE_OUT_OF_MEMORY :
    671         Curl_auth_gsasl_token(data, &serverdata, gsasl, &resp);
    672     }
    673     if(!result && Curl_bufref_len(&resp) > 0)
    674       newstate = SASL_GSASL;
    675     break;
    676 #endif
    677 #ifndef CURL_DISABLE_DIGEST_AUTH
    678   case SASL_CRAMMD5:
    679     result = get_server_message(sasl, data, &serverdata);
    680     if(!result)
    681       result = Curl_auth_create_cram_md5_message(&serverdata, conn->user,
    682                                                  conn->passwd, &resp);
    683     break;
    684   case SASL_DIGESTMD5:
    685     result = get_server_message(sasl, data, &serverdata);
    686     if(!result)
    687       result = Curl_auth_create_digest_md5_message(data, &serverdata,
    688                                                    conn->user, conn->passwd,
    689                                                    service, &resp);
    690     if(!result && (sasl->params->flags & SASL_FLAG_BASE64))
    691       newstate = SASL_DIGESTMD5_RESP;
    692     break;
    693   case SASL_DIGESTMD5_RESP:
    694     /* Keep response NULL to output an empty line. */
    695     break;
    696 #endif
    697 
    698 #ifdef USE_NTLM
    699   case SASL_NTLM: {
    700     /* Create the type-1 message */
    701     struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE);
    702     result = !ntlm ? CURLE_OUT_OF_MEMORY :
    703       Curl_auth_create_ntlm_type1_message(data,
    704                                           conn->user, conn->passwd,
    705                                           service, hostname,
    706                                           ntlm, &resp);
    707     newstate = SASL_NTLM_TYPE2MSG;
    708     break;
    709   }
    710   case SASL_NTLM_TYPE2MSG: {
    711     /* Decode the type-2 message */
    712     struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE);
    713     result = !ntlm ? CURLE_FAILED_INIT :
    714       get_server_message(sasl, data, &serverdata);
    715     if(!result)
    716       result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm);
    717     if(!result)
    718       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
    719                                                    conn->passwd, ntlm,
    720                                                    &resp);
    721     break;
    722   }
    723 #endif
    724 
    725 #if defined(USE_KERBEROS5)
    726   case SASL_GSSAPI: {
    727     struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
    728     result = !krb5 ? CURLE_OUT_OF_MEMORY :
    729       Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd,
    730                                                   service, conn->host.name,
    731                                                   sasl->mutual_auth, NULL,
    732                                                   krb5, &resp);
    733     newstate = SASL_GSSAPI_TOKEN;
    734     break;
    735   }
    736   case SASL_GSSAPI_TOKEN:
    737     result = get_server_message(sasl, data, &serverdata);
    738     if(!result) {
    739       struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
    740       if(!krb5)
    741         result = CURLE_OUT_OF_MEMORY;
    742       else if(sasl->mutual_auth) {
    743         /* Decode the user token challenge and create the optional response
    744            message */
    745         result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
    746                                                       NULL, NULL,
    747                                                       sasl->mutual_auth,
    748                                                       &serverdata,
    749                                                       krb5, &resp);
    750         newstate = SASL_GSSAPI_NO_DATA;
    751       }
    752       else
    753         /* Decode the security challenge and create the response message */
    754         result = Curl_auth_create_gssapi_security_message(data,
    755                                                           conn->sasl_authzid,
    756                                                           &serverdata,
    757                                                           krb5, &resp);
    758     }
    759     break;
    760   case SASL_GSSAPI_NO_DATA:
    761     /* Decode the security challenge and create the response message */
    762     result = get_server_message(sasl, data, &serverdata);
    763     if(!result) {
    764       struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
    765       if(!krb5)
    766         result = CURLE_OUT_OF_MEMORY;
    767       else
    768         result = Curl_auth_create_gssapi_security_message(data,
    769                                                           conn->sasl_authzid,
    770                                                           &serverdata,
    771                                                           krb5, &resp);
    772     }
    773     break;
    774 #endif
    775 
    776   case SASL_OAUTH2:
    777     /* Create the authorization message */
    778     if(sasl->authused == SASL_MECH_OAUTHBEARER) {
    779       result = Curl_auth_create_oauth_bearer_message(conn->user,
    780                                                      hostname,
    781                                                      port,
    782                                                      oauth_bearer,
    783                                                      &resp);
    784 
    785       /* Failures maybe sent by the server as continuations for OAUTHBEARER */
    786       newstate = SASL_OAUTH2_RESP;
    787     }
    788     else
    789       result = Curl_auth_create_xoauth_bearer_message(conn->user,
    790                                                       oauth_bearer,
    791                                                       &resp);
    792     break;
    793 
    794   case SASL_OAUTH2_RESP:
    795     /* The continuation is optional so check the response code */
    796     if(code == sasl->params->finalcode) {
    797       /* Final response was received so we are done */
    798       *progress = SASL_DONE;
    799       sasl_state(sasl, data, SASL_STOP);
    800       return result;
    801     }
    802     else if(code == sasl->params->contcode) {
    803       /* Acknowledge the continuation by sending a 0x01 response. */
    804       Curl_bufref_set(&resp, "\x01", 1, NULL);
    805       break;
    806     }
    807     else {
    808       *progress = SASL_DONE;
    809       sasl_state(sasl, data, SASL_STOP);
    810       return CURLE_LOGIN_DENIED;
    811     }
    812 
    813   case SASL_CANCEL:
    814     /* Remove the offending mechanism from the supported list */
    815     sasl->authmechs ^= sasl->authused;
    816 
    817     /* Start an alternative SASL authentication */
    818     return Curl_sasl_start(sasl, data, sasl->force_ir, progress);
    819   default:
    820     failf(data, "Unsupported SASL authentication mechanism");
    821     result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
    822     break;
    823   }
    824 
    825   Curl_bufref_free(&serverdata);
    826 
    827   switch(result) {
    828   case CURLE_BAD_CONTENT_ENCODING:
    829     /* Cancel dialog */
    830     result = sasl->params->cancelauth(data, sasl->curmech);
    831     newstate = SASL_CANCEL;
    832     break;
    833   case CURLE_OK:
    834     result = build_message(sasl, &resp);
    835     if(!result)
    836       result = sasl->params->contauth(data, sasl->curmech, &resp);
    837     break;
    838   default:
    839     newstate = SASL_STOP;    /* Stop on error */
    840     *progress = SASL_DONE;
    841     break;
    842   }
    843 
    844   Curl_bufref_free(&resp);
    845 
    846   sasl_state(sasl, data, newstate);
    847 
    848   return result;
    849 }
    850 
    851 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    852 static void sasl_unchosen(struct Curl_easy *data, unsigned short mech,
    853                           unsigned short enabledmechs,
    854                           bool built_in, bool platform,
    855                           const char *param_missing)
    856 {
    857   const char *mname = NULL;
    858   size_t i;
    859 
    860   if(!(enabledmechs & mech))
    861     return;
    862 
    863   for(i = 0; mechtable[i].name; ++i) {
    864     if(mechtable[i].bit == mech) {
    865       mname = mechtable[i].name;
    866       break;
    867     }
    868   }
    869   if(!mname)  /* should not happen */
    870     return;
    871   if(!built_in)
    872     infof(data, "SASL: %s not builtin", mname);
    873   else if(!platform)
    874     infof(data, "SASL: %s not supported by the platform/libraries", mname);
    875   else {
    876     if(param_missing)
    877       infof(data, "SASL: %s is missing %s", mname, param_missing);
    878     if(!data->state.aptr.user)
    879       infof(data, "SASL: %s is missing username", mname);
    880   }
    881 }
    882 #endif /* CURL_DISABLE_VERBOSE_STRINGS */
    883 
    884 CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data)
    885 {
    886 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    887 #ifdef USE_KERBEROS5
    888 #define CURL_SASL_KERBEROS5   TRUE
    889 #else
    890 #define CURL_SASL_KERBEROS5   FALSE
    891 #endif
    892 #ifdef USE_GSASL
    893 #define CURL_SASL_GASL        TRUE
    894 #else
    895 #define CURL_SASL_GASL        FALSE
    896 #endif
    897 #ifdef CURL_DISABLE_DIGEST_AUTH
    898 #define CURL_SASL_DIGEST      TRUE
    899 #else
    900 #define CURL_SASL_DIGEST      FALSE
    901 #endif
    902 #ifndef USE_NTLM
    903 #define CURL_SASL_NTLM        TRUE
    904 #else
    905 #define CURL_SASL_NTLM        FALSE
    906 #endif
    907   /* Failing SASL authentication is a pain. Give a helping hand if
    908    * we were unable to select an AUTH mechanism.
    909    * `sasl->authmechs` are mechanisms offered by the peer
    910    * `sasl->prefmech`  are mechanisms preferred by us */
    911   unsigned short enabledmechs = sasl->authmechs & sasl->prefmech;
    912 
    913   if(!sasl->authmechs)
    914     infof(data, "SASL: no auth mechanism was offered or recognized");
    915   else if(!enabledmechs)
    916     infof(data, "SASL: no overlap between offered and configured "
    917           "auth mechanisms");
    918   else {
    919     infof(data, "SASL: no auth mechanism offered could be selected");
    920     if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0])
    921       infof(data, "SASL: auth EXTERNAL not chosen with password");
    922     sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs,
    923                   CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL);
    924     sasl_unchosen(data, SASL_MECH_SCRAM_SHA_256, enabledmechs,
    925                   CURL_SASL_GASL, FALSE, NULL);
    926     sasl_unchosen(data, SASL_MECH_SCRAM_SHA_1, enabledmechs,
    927                   CURL_SASL_GASL, FALSE, NULL);
    928     sasl_unchosen(data, SASL_MECH_DIGEST_MD5, enabledmechs,
    929                   CURL_SASL_DIGEST, Curl_auth_is_digest_supported(), NULL);
    930     sasl_unchosen(data, SASL_MECH_CRAM_MD5, enabledmechs,
    931                   CURL_SASL_DIGEST, TRUE, NULL);
    932     sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs,
    933                   CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL);
    934     sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs,  TRUE, TRUE,
    935                   data->set.str[STRING_BEARER] ?
    936                   NULL : "CURLOPT_XOAUTH2_BEARER");
    937     sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs,  TRUE, TRUE,
    938                   data->set.str[STRING_BEARER] ?
    939                   NULL : "CURLOPT_XOAUTH2_BEARER");
    940   }
    941 #endif /* CURL_DISABLE_VERBOSE_STRINGS */
    942   (void)sasl;
    943   (void)data;
    944   return CURLE_LOGIN_DENIED;
    945 }
    946 
    947 #endif /* protocols are enabled that use SASL */