Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel (at) 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 http://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * RFC2195 CRAM-MD5 authentication
     22  * RFC2617 Basic and Digest Access Authentication
     23  * RFC2831 DIGEST-MD5 authentication
     24  * RFC4422 Simple Authentication and Security Layer (SASL)
     25  * RFC4616 PLAIN authentication
     26  * RFC6749 OAuth 2.0 Authorization Framework
     27  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
     28  *
     29  ***************************************************************************/
     30 
     31 #include "curl_setup.h"
     32 
     33 #include <curl/curl.h>
     34 #include "urldata.h"
     35 
     36 #include "curl_base64.h"
     37 #include "curl_md5.h"
     38 #include "vtls/vtls.h"
     39 #include "curl_hmac.h"
     40 #include "curl_sasl.h"
     41 #include "warnless.h"
     42 #include "strtok.h"
     43 #include "strequal.h"
     44 #include "rawstr.h"
     45 #include "sendf.h"
     46 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
     47 #include "curl_printf.h"
     48 
     49 /* The last #include files should be: */
     50 #include "curl_memory.h"
     51 #include "memdebug.h"
     52 
     53 /* Supported mechanisms */
     54 const struct {
     55   const char   *name;  /* Name */
     56   size_t        len;   /* Name length */
     57   unsigned int  bit;   /* Flag bit */
     58 } mechtable[] = {
     59   { "LOGIN",      5,  SASL_MECH_LOGIN },
     60   { "PLAIN",      5,  SASL_MECH_PLAIN },
     61   { "CRAM-MD5",   8,  SASL_MECH_CRAM_MD5 },
     62   { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 },
     63   { "GSSAPI",     6,  SASL_MECH_GSSAPI },
     64   { "EXTERNAL",   8,  SASL_MECH_EXTERNAL },
     65   { "NTLM",       4,  SASL_MECH_NTLM },
     66   { "XOAUTH2",    7,  SASL_MECH_XOAUTH2 },
     67   { ZERO_NULL,    0,  0 }
     68 };
     69 
     70 #if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI)
     71 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
     72 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
     73 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
     74 
     75 #define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
     76 #define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
     77 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
     78 
     79 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
     80    It converts digest text to ASCII so the MD5 will be correct for
     81    what ultimately goes over the network.
     82 */
     83 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
     84   result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
     85   if(result) { \
     86     free(b); \
     87     return result; \
     88   }
     89 
     90 #endif
     91 
     92 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
     93 /*
     94  * Returns 0 on success and then the buffers are filled in fine.
     95  *
     96  * Non-zero means failure to parse.
     97  */
     98 int Curl_sasl_digest_get_pair(const char *str, char *value, char *content,
     99                               const char **endptr)
    100 {
    101   int c;
    102   bool starts_with_quote = FALSE;
    103   bool escape = FALSE;
    104 
    105   for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--); )
    106     *value++ = *str++;
    107   *value = 0;
    108 
    109   if('=' != *str++)
    110     /* eek, no match */
    111     return 1;
    112 
    113   if('\"' == *str) {
    114     /* this starts with a quote so it must end with one as well! */
    115     str++;
    116     starts_with_quote = TRUE;
    117   }
    118 
    119   for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
    120     switch(*str) {
    121     case '\\':
    122       if(!escape) {
    123         /* possibly the start of an escaped quote */
    124         escape = TRUE;
    125         *content++ = '\\'; /* even though this is an escape character, we still
    126                               store it as-is in the target buffer */
    127         continue;
    128       }
    129       break;
    130     case ',':
    131       if(!starts_with_quote) {
    132         /* this signals the end of the content if we didn't get a starting
    133            quote and then we do "sloppy" parsing */
    134         c = 0; /* the end */
    135         continue;
    136       }
    137       break;
    138     case '\r':
    139     case '\n':
    140       /* end of string */
    141       c = 0;
    142       continue;
    143     case '\"':
    144       if(!escape && starts_with_quote) {
    145         /* end of string */
    146         c = 0;
    147         continue;
    148       }
    149       break;
    150     }
    151     escape = FALSE;
    152     *content++ = *str;
    153   }
    154   *content = 0;
    155 
    156   *endptr = str;
    157 
    158   return 0; /* all is fine! */
    159 }
    160 #endif
    161 
    162 #if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI)
    163 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
    164 static void sasl_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
    165                                      unsigned char *dest) /* 33 bytes */
    166 {
    167   int i;
    168   for(i = 0; i < 16; i++)
    169     snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
    170 }
    171 
    172 /* Perform quoted-string escaping as described in RFC2616 and its errata */
    173 static char *sasl_digest_string_quoted(const char *source)
    174 {
    175   char *dest, *d;
    176   const char *s = source;
    177   size_t n = 1; /* null terminator */
    178 
    179   /* Calculate size needed */
    180   while(*s) {
    181     ++n;
    182     if(*s == '"' || *s == '\\') {
    183       ++n;
    184     }
    185     ++s;
    186   }
    187 
    188   dest = malloc(n);
    189   if(dest) {
    190     s = source;
    191     d = dest;
    192     while(*s) {
    193       if(*s == '"' || *s == '\\') {
    194         *d++ = '\\';
    195       }
    196       *d++ = *s++;
    197     }
    198     *d = 0;
    199   }
    200 
    201   return dest;
    202 }
    203 
    204 /* Retrieves the value for a corresponding key from the challenge string
    205  * returns TRUE if the key could be found, FALSE if it does not exists
    206  */
    207 static bool sasl_digest_get_key_value(const char *chlg,
    208                                       const char *key,
    209                                       char *value,
    210                                       size_t max_val_len,
    211                                       char end_char)
    212 {
    213   char *find_pos;
    214   size_t i;
    215 
    216   find_pos = strstr(chlg, key);
    217   if(!find_pos)
    218     return FALSE;
    219 
    220   find_pos += strlen(key);
    221 
    222   for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
    223     value[i] = *find_pos++;
    224   value[i] = '\0';
    225 
    226   return TRUE;
    227 }
    228 
    229 static CURLcode sasl_digest_get_qop_values(const char *options, int *value)
    230 {
    231   char *tmp;
    232   char *token;
    233   char *tok_buf;
    234 
    235   /* Initialise the output */
    236   *value = 0;
    237 
    238   /* Tokenise the list of qop values. Use a temporary clone of the buffer since
    239      strtok_r() ruins it. */
    240   tmp = strdup(options);
    241   if(!tmp)
    242     return CURLE_OUT_OF_MEMORY;
    243 
    244   token = strtok_r(tmp, ",", &tok_buf);
    245   while(token != NULL) {
    246     if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH))
    247       *value |= DIGEST_QOP_VALUE_AUTH;
    248     else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
    249       *value |= DIGEST_QOP_VALUE_AUTH_INT;
    250     else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
    251       *value |= DIGEST_QOP_VALUE_AUTH_CONF;
    252 
    253     token = strtok_r(NULL, ",", &tok_buf);
    254   }
    255 
    256   free(tmp);
    257 
    258   return CURLE_OK;
    259 }
    260 #endif /* !CURL_DISABLE_CRYPTO_AUTH && !USE_WINDOWS_SSPI */
    261 
    262 #if !defined(USE_WINDOWS_SSPI)
    263 /*
    264  * Curl_sasl_build_spn()
    265  *
    266  * This is used to build a SPN string in the format service/host.
    267  *
    268  * Parameters:
    269  *
    270  * service  [in] - The service type such as www, smtp, pop or imap.
    271  * host     [in] - The host name or realm.
    272  *
    273  * Returns a pointer to the newly allocated SPN.
    274  */
    275 char *Curl_sasl_build_spn(const char *service, const char *host)
    276 {
    277   /* Generate and return our SPN */
    278   return aprintf("%s/%s", service, host);
    279 }
    280 #endif
    281 
    282 /*
    283  * sasl_create_plain_message()
    284  *
    285  * This is used to generate an already encoded PLAIN message ready
    286  * for sending to the recipient.
    287  *
    288  * Parameters:
    289  *
    290  * data    [in]     - The session handle.
    291  * userp   [in]     - The user name.
    292  * passdwp [in]     - The user's password.
    293  * outptr  [in/out] - The address where a pointer to newly allocated memory
    294  *                    holding the result will be stored upon completion.
    295  * outlen  [out]    - The length of the output message.
    296  *
    297  * Returns CURLE_OK on success.
    298  */
    299 static CURLcode sasl_create_plain_message(struct SessionHandle *data,
    300                                           const char *userp,
    301                                           const char *passwdp,
    302                                           char **outptr, size_t *outlen)
    303 {
    304   CURLcode result;
    305   char *plainauth;
    306   size_t ulen;
    307   size_t plen;
    308 
    309   ulen = strlen(userp);
    310   plen = strlen(passwdp);
    311 
    312   plainauth = malloc(2 * ulen + plen + 2);
    313   if(!plainauth) {
    314     *outlen = 0;
    315     *outptr = NULL;
    316     return CURLE_OUT_OF_MEMORY;
    317   }
    318 
    319   /* Calculate the reply */
    320   memcpy(plainauth, userp, ulen);
    321   plainauth[ulen] = '\0';
    322   memcpy(plainauth + ulen + 1, userp, ulen);
    323   plainauth[2 * ulen + 1] = '\0';
    324   memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
    325 
    326   /* Base64 encode the reply */
    327   result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr,
    328                               outlen);
    329   free(plainauth);
    330   return result;
    331 }
    332 
    333 /*
    334  * sasl_create_login_message()
    335  *
    336  * This is used to generate an already encoded LOGIN message containing the
    337  * user name or password ready for sending to the recipient.
    338  *
    339  * Parameters:
    340  *
    341  * data    [in]     - The session handle.
    342  * valuep  [in]     - The user name or user's password.
    343  * outptr  [in/out] - The address where a pointer to newly allocated memory
    344  *                    holding the result will be stored upon completion.
    345  * outlen  [out]    - The length of the output message.
    346  *
    347  * Returns CURLE_OK on success.
    348  */
    349 static CURLcode sasl_create_login_message(struct SessionHandle *data,
    350                                           const char *valuep, char **outptr,
    351                                           size_t *outlen)
    352 {
    353   size_t vlen = strlen(valuep);
    354 
    355   if(!vlen) {
    356     /* Calculate an empty reply */
    357     *outptr = strdup("=");
    358     if(*outptr) {
    359       *outlen = (size_t) 1;
    360       return CURLE_OK;
    361     }
    362 
    363     *outlen = 0;
    364     return CURLE_OUT_OF_MEMORY;
    365   }
    366 
    367   /* Base64 encode the value */
    368   return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
    369 }
    370 
    371 /*
    372  * sasl_create_external_message()
    373  *
    374  * This is used to generate an already encoded EXTERNAL message containing
    375  * the user name ready for sending to the recipient.
    376  *
    377  * Parameters:
    378  *
    379  * data    [in]     - The session handle.
    380  * user    [in]     - The user name.
    381  * outptr  [in/out] - The address where a pointer to newly allocated memory
    382  *                    holding the result will be stored upon completion.
    383  * outlen  [out]    - The length of the output message.
    384  *
    385  * Returns CURLE_OK on success.
    386  */
    387 static CURLcode sasl_create_external_message(struct SessionHandle *data,
    388                                              const char *user, char **outptr,
    389                                              size_t *outlen)
    390 {
    391   /* This is the same formatting as the login message. */
    392   return sasl_create_login_message(data, user, outptr, outlen);
    393 }
    394 
    395 #ifndef CURL_DISABLE_CRYPTO_AUTH
    396  /*
    397  * sasl_decode_cram_md5_message()
    398  *
    399  * This is used to decode an already encoded CRAM-MD5 challenge message.
    400  *
    401  * Parameters:
    402  *
    403  * chlg64  [in]     - The base64 encoded challenge message.
    404  * outptr  [in/out] - The address where a pointer to newly allocated memory
    405  *                    holding the result will be stored upon completion.
    406  * outlen  [out]    - The length of the output message.
    407  *
    408  * Returns CURLE_OK on success.
    409  */
    410 static CURLcode sasl_decode_cram_md5_message(const char *chlg64, char **outptr,
    411                                              size_t *outlen)
    412 {
    413   CURLcode result = CURLE_OK;
    414   size_t chlg64len = strlen(chlg64);
    415 
    416   *outptr = NULL;
    417   *outlen = 0;
    418 
    419   /* Decode the challenge if necessary */
    420   if(chlg64len && *chlg64 != '=')
    421     result = Curl_base64_decode(chlg64, (unsigned char **) outptr, outlen);
    422 
    423     return result;
    424  }
    425 
    426  /*
    427  * sasl_create_cram_md5_message()
    428  *
    429  * This is used to generate an already encoded CRAM-MD5 response message ready
    430  * for sending to the recipient.
    431  *
    432  * Parameters:
    433  *
    434  * data    [in]     - The session handle.
    435  * chlg    [in]     - The challenge.
    436  * userp   [in]     - The user name.
    437  * passdwp [in]     - The user's password.
    438  * outptr  [in/out] - The address where a pointer to newly allocated memory
    439  *                    holding the result will be stored upon completion.
    440  * outlen  [out]    - The length of the output message.
    441  *
    442  * Returns CURLE_OK on success.
    443  */
    444 static CURLcode sasl_create_cram_md5_message(struct SessionHandle *data,
    445                                              const char *chlg,
    446                                              const char *userp,
    447                                              const char *passwdp,
    448                                              char **outptr, size_t *outlen)
    449 {
    450   CURLcode result = CURLE_OK;
    451   size_t chlglen = 0;
    452   HMAC_context *ctxt;
    453   unsigned char digest[MD5_DIGEST_LEN];
    454   char *response;
    455 
    456   if(chlg)
    457     chlglen = strlen(chlg);
    458 
    459   /* Compute the digest using the password as the key */
    460   ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
    461                         (const unsigned char *) passwdp,
    462                         curlx_uztoui(strlen(passwdp)));
    463   if(!ctxt)
    464     return CURLE_OUT_OF_MEMORY;
    465 
    466   /* Update the digest with the given challenge */
    467   if(chlglen > 0)
    468     Curl_HMAC_update(ctxt, (const unsigned char *) chlg,
    469                      curlx_uztoui(chlglen));
    470 
    471   /* Finalise the digest */
    472   Curl_HMAC_final(ctxt, digest);
    473 
    474   /* Generate the response */
    475   response = aprintf(
    476       "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
    477            userp, digest[0], digest[1], digest[2], digest[3], digest[4],
    478            digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
    479            digest[11], digest[12], digest[13], digest[14], digest[15]);
    480   if(!response)
    481     return CURLE_OUT_OF_MEMORY;
    482 
    483   /* Base64 encode the response */
    484   result = Curl_base64_encode(data, response, 0, outptr, outlen);
    485 
    486   free(response);
    487 
    488   return result;
    489 }
    490 
    491 #ifndef USE_WINDOWS_SSPI
    492 /*
    493  * sasl_decode_digest_md5_message()
    494  *
    495  * This is used internally to decode an already encoded DIGEST-MD5 challenge
    496  * message into the seperate attributes.
    497  *
    498  * Parameters:
    499  *
    500  * chlg64  [in]     - The base64 encoded challenge message.
    501  * nonce   [in/out] - The buffer where the nonce will be stored.
    502  * nlen    [in]     - The length of the nonce buffer.
    503  * realm   [in/out] - The buffer where the realm will be stored.
    504  * rlen    [in]     - The length of the realm buffer.
    505  * alg     [in/out] - The buffer where the algorithm will be stored.
    506  * alen    [in]     - The length of the algorithm buffer.
    507  * qop     [in/out] - The buffer where the qop-options will be stored.
    508  * qlen    [in]     - The length of the qop buffer.
    509  *
    510  * Returns CURLE_OK on success.
    511  */
    512 static CURLcode sasl_decode_digest_md5_message(const char *chlg64,
    513                                                char *nonce, size_t nlen,
    514                                                char *realm, size_t rlen,
    515                                                char *alg, size_t alen,
    516                                                char *qop, size_t qlen)
    517 {
    518   CURLcode result = CURLE_OK;
    519   unsigned char *chlg = NULL;
    520   size_t chlglen = 0;
    521   size_t chlg64len = strlen(chlg64);
    522 
    523   /* Decode the base-64 encoded challenge message */
    524   if(chlg64len && *chlg64 != '=') {
    525     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
    526     if(result)
    527       return result;
    528   }
    529 
    530   /* Ensure we have a valid challenge message */
    531   if(!chlg)
    532     return CURLE_BAD_CONTENT_ENCODING;
    533 
    534   /* Retrieve nonce string from the challenge */
    535   if(!sasl_digest_get_key_value((char *)chlg, "nonce=\"", nonce, nlen, '\"')) {
    536     free(chlg);
    537     return CURLE_BAD_CONTENT_ENCODING;
    538   }
    539 
    540   /* Retrieve realm string from the challenge */
    541   if(!sasl_digest_get_key_value((char *)chlg, "realm=\"", realm, rlen, '\"')) {
    542     /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
    543     strcpy(realm, "");
    544   }
    545 
    546   /* Retrieve algorithm string from the challenge */
    547   if(!sasl_digest_get_key_value((char *)chlg, "algorithm=", alg, alen, ',')) {
    548     free(chlg);
    549     return CURLE_BAD_CONTENT_ENCODING;
    550   }
    551 
    552   /* Retrieve qop-options string from the challenge */
    553   if(!sasl_digest_get_key_value((char *)chlg, "qop=\"", qop, qlen, '\"')) {
    554     free(chlg);
    555     return CURLE_BAD_CONTENT_ENCODING;
    556   }
    557 
    558   free(chlg);
    559 
    560   return CURLE_OK;
    561 }
    562 
    563 /*
    564  * Curl_sasl_create_digest_md5_message()
    565  *
    566  * This is used to generate an already encoded DIGEST-MD5 response message
    567  * ready for sending to the recipient.
    568  *
    569  * Parameters:
    570  *
    571  * data    [in]     - The session handle.
    572  * chlg64  [in]     - The base64 encoded challenge message.
    573  * userp   [in]     - The user name.
    574  * passdwp [in]     - The user's password.
    575  * service [in]     - The service type such as www, smtp, pop or imap.
    576  * outptr  [in/out] - The address where a pointer to newly allocated memory
    577  *                    holding the result will be stored upon completion.
    578  * outlen  [out]    - The length of the output message.
    579  *
    580  * Returns CURLE_OK on success.
    581  */
    582 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
    583                                              const char *chlg64,
    584                                              const char *userp,
    585                                              const char *passwdp,
    586                                              const char *service,
    587                                              char **outptr, size_t *outlen)
    588 {
    589   CURLcode result = CURLE_OK;
    590   size_t i;
    591   MD5_context *ctxt;
    592   char *response = NULL;
    593   unsigned char digest[MD5_DIGEST_LEN];
    594   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
    595   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
    596   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
    597   char nonce[64];
    598   char realm[128];
    599   char algorithm[64];
    600   char qop_options[64];
    601   int qop_values;
    602   char cnonce[33];
    603   unsigned int entropy[4];
    604   char nonceCount[] = "00000001";
    605   char method[]     = "AUTHENTICATE";
    606   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
    607   char *spn         = NULL;
    608 
    609   /* Decode the challange message */
    610   result = sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
    611                                           realm, sizeof(realm),
    612                                           algorithm, sizeof(algorithm),
    613                                           qop_options, sizeof(qop_options));
    614   if(result)
    615     return result;
    616 
    617   /* We only support md5 sessions */
    618   if(strcmp(algorithm, "md5-sess") != 0)
    619     return CURLE_BAD_CONTENT_ENCODING;
    620 
    621   /* Get the qop-values from the qop-options */
    622   result = sasl_digest_get_qop_values(qop_options, &qop_values);
    623   if(result)
    624     return result;
    625 
    626   /* We only support auth quality-of-protection */
    627   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
    628     return CURLE_BAD_CONTENT_ENCODING;
    629 
    630   /* Generate 16 bytes of random data */
    631   entropy[0] = Curl_rand(data);
    632   entropy[1] = Curl_rand(data);
    633   entropy[2] = Curl_rand(data);
    634   entropy[3] = Curl_rand(data);
    635 
    636   /* Convert the random data into a 32 byte hex string */
    637   snprintf(cnonce, sizeof(cnonce), "%08x%08x%08x%08x",
    638            entropy[0], entropy[1], entropy[2], entropy[3]);
    639 
    640   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
    641   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
    642   if(!ctxt)
    643     return CURLE_OUT_OF_MEMORY;
    644 
    645   Curl_MD5_update(ctxt, (const unsigned char *) userp,
    646                   curlx_uztoui(strlen(userp)));
    647   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    648   Curl_MD5_update(ctxt, (const unsigned char *) realm,
    649                   curlx_uztoui(strlen(realm)));
    650   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    651   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
    652                   curlx_uztoui(strlen(passwdp)));
    653   Curl_MD5_final(ctxt, digest);
    654 
    655   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
    656   if(!ctxt)
    657     return CURLE_OUT_OF_MEMORY;
    658 
    659   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
    660   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    661   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
    662                   curlx_uztoui(strlen(nonce)));
    663   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    664   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
    665                   curlx_uztoui(strlen(cnonce)));
    666   Curl_MD5_final(ctxt, digest);
    667 
    668   /* Convert calculated 16 octet hex into 32 bytes string */
    669   for(i = 0; i < MD5_DIGEST_LEN; i++)
    670     snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
    671 
    672   /* Generate our SPN */
    673   spn = Curl_sasl_build_spn(service, realm);
    674   if(!spn)
    675     return CURLE_OUT_OF_MEMORY;
    676 
    677   /* Calculate H(A2) */
    678   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
    679   if(!ctxt) {
    680     free(spn);
    681 
    682     return CURLE_OUT_OF_MEMORY;
    683   }
    684 
    685   Curl_MD5_update(ctxt, (const unsigned char *) method,
    686                   curlx_uztoui(strlen(method)));
    687   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    688   Curl_MD5_update(ctxt, (const unsigned char *) spn,
    689                   curlx_uztoui(strlen(spn)));
    690   Curl_MD5_final(ctxt, digest);
    691 
    692   for(i = 0; i < MD5_DIGEST_LEN; i++)
    693     snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
    694 
    695   /* Now calculate the response hash */
    696   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
    697   if(!ctxt) {
    698     free(spn);
    699 
    700     return CURLE_OUT_OF_MEMORY;
    701   }
    702 
    703   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
    704   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    705   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
    706                   curlx_uztoui(strlen(nonce)));
    707   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    708 
    709   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
    710                   curlx_uztoui(strlen(nonceCount)));
    711   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    712   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
    713                   curlx_uztoui(strlen(cnonce)));
    714   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    715   Curl_MD5_update(ctxt, (const unsigned char *) qop,
    716                   curlx_uztoui(strlen(qop)));
    717   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
    718 
    719   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
    720   Curl_MD5_final(ctxt, digest);
    721 
    722   for(i = 0; i < MD5_DIGEST_LEN; i++)
    723     snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
    724 
    725   /* Generate the response */
    726   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
    727                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
    728                      "qop=%s",
    729                      userp, realm, nonce,
    730                      cnonce, nonceCount, spn, resp_hash_hex, qop);
    731   free(spn);
    732   if(!response)
    733     return CURLE_OUT_OF_MEMORY;
    734 
    735   /* Base64 encode the response */
    736   result = Curl_base64_encode(data, response, 0, outptr, outlen);
    737 
    738   free(response);
    739 
    740   return result;
    741 }
    742 
    743 /*
    744  * Curl_sasl_decode_digest_http_message()
    745  *
    746  * This is used to decode a HTTP DIGEST challenge message into the seperate
    747  * attributes.
    748  *
    749  * Parameters:
    750  *
    751  * chlg    [in]     - The challenge message.
    752  * digest  [in/out] - The digest data struct being used and modified.
    753  *
    754  * Returns CURLE_OK on success.
    755  */
    756 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
    757                                               struct digestdata *digest)
    758 {
    759   bool before = FALSE; /* got a nonce before */
    760   bool foundAuth = FALSE;
    761   bool foundAuthInt = FALSE;
    762   char *token = NULL;
    763   char *tmp = NULL;
    764 
    765   /* If we already have received a nonce, keep that in mind */
    766   if(digest->nonce)
    767     before = TRUE;
    768 
    769   /* Clean up any former leftovers and initialise to defaults */
    770   Curl_sasl_digest_cleanup(digest);
    771 
    772   for(;;) {
    773     char value[DIGEST_MAX_VALUE_LENGTH];
    774     char content[DIGEST_MAX_CONTENT_LENGTH];
    775 
    776     /* Pass all additional spaces here */
    777     while(*chlg && ISSPACE(*chlg))
    778       chlg++;
    779 
    780     /* Extract a value=content pair */
    781     if(!Curl_sasl_digest_get_pair(chlg, value, content, &chlg)) {
    782       if(Curl_raw_equal(value, "nonce")) {
    783         digest->nonce = strdup(content);
    784         if(!digest->nonce)
    785           return CURLE_OUT_OF_MEMORY;
    786       }
    787       else if(Curl_raw_equal(value, "stale")) {
    788         if(Curl_raw_equal(content, "true")) {
    789           digest->stale = TRUE;
    790           digest->nc = 1; /* we make a new nonce now */
    791         }
    792       }
    793       else if(Curl_raw_equal(value, "realm")) {
    794         digest->realm = strdup(content);
    795         if(!digest->realm)
    796           return CURLE_OUT_OF_MEMORY;
    797       }
    798       else if(Curl_raw_equal(value, "opaque")) {
    799         digest->opaque = strdup(content);
    800         if(!digest->opaque)
    801           return CURLE_OUT_OF_MEMORY;
    802       }
    803       else if(Curl_raw_equal(value, "qop")) {
    804         char *tok_buf;
    805         /* Tokenize the list and choose auth if possible, use a temporary
    806             clone of the buffer since strtok_r() ruins it */
    807         tmp = strdup(content);
    808         if(!tmp)
    809           return CURLE_OUT_OF_MEMORY;
    810 
    811         token = strtok_r(tmp, ",", &tok_buf);
    812         while(token != NULL) {
    813           if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
    814             foundAuth = TRUE;
    815           }
    816           else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
    817             foundAuthInt = TRUE;
    818           }
    819           token = strtok_r(NULL, ",", &tok_buf);
    820         }
    821 
    822         free(tmp);
    823 
    824         /* Select only auth or auth-int. Otherwise, ignore */
    825         if(foundAuth) {
    826           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
    827           if(!digest->qop)
    828             return CURLE_OUT_OF_MEMORY;
    829         }
    830         else if(foundAuthInt) {
    831           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
    832           if(!digest->qop)
    833             return CURLE_OUT_OF_MEMORY;
    834         }
    835       }
    836       else if(Curl_raw_equal(value, "algorithm")) {
    837         digest->algorithm = strdup(content);
    838         if(!digest->algorithm)
    839           return CURLE_OUT_OF_MEMORY;
    840 
    841         if(Curl_raw_equal(content, "MD5-sess"))
    842           digest->algo = CURLDIGESTALGO_MD5SESS;
    843         else if(Curl_raw_equal(content, "MD5"))
    844           digest->algo = CURLDIGESTALGO_MD5;
    845         else
    846           return CURLE_BAD_CONTENT_ENCODING;
    847       }
    848       else {
    849         /* unknown specifier, ignore it! */
    850       }
    851     }
    852     else
    853       break; /* we're done here */
    854 
    855     /* Pass all additional spaces here */
    856     while(*chlg && ISSPACE(*chlg))
    857       chlg++;
    858 
    859     /* Allow the list to be comma-separated */
    860     if(',' == *chlg)
    861       chlg++;
    862   }
    863 
    864   /* We had a nonce since before, and we got another one now without
    865      'stale=true'. This means we provided bad credentials in the previous
    866      request */
    867   if(before && !digest->stale)
    868     return CURLE_BAD_CONTENT_ENCODING;
    869 
    870   /* We got this header without a nonce, that's a bad Digest line! */
    871   if(!digest->nonce)
    872     return CURLE_BAD_CONTENT_ENCODING;
    873 
    874   return CURLE_OK;
    875 }
    876 
    877 /*
    878  * Curl_sasl_create_digest_http_message()
    879  *
    880  * This is used to generate a HTTP DIGEST response message ready for sending
    881  * to the recipient.
    882  *
    883  * Parameters:
    884  *
    885  * data    [in]     - The session handle.
    886  * userp   [in]     - The user name.
    887  * passdwp [in]     - The user's password.
    888  * request [in]     - The HTTP request.
    889  * uripath [in]     - The path of the HTTP uri.
    890  * digest  [in/out] - The digest data struct being used and modified.
    891  * outptr  [in/out] - The address where a pointer to newly allocated memory
    892  *                    holding the result will be stored upon completion.
    893  * outlen  [out]    - The length of the output message.
    894  *
    895  * Returns CURLE_OK on success.
    896  */
    897 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
    898                                               const char *userp,
    899                                               const char *passwdp,
    900                                               const unsigned char *request,
    901                                               const unsigned char *uripath,
    902                                               struct digestdata *digest,
    903                                               char **outptr, size_t *outlen)
    904 {
    905   CURLcode result;
    906   unsigned char md5buf[16]; /* 16 bytes/128 bits */
    907   unsigned char request_digest[33];
    908   unsigned char *md5this;
    909   unsigned char ha1[33];/* 32 digits and 1 zero byte */
    910   unsigned char ha2[33];/* 32 digits and 1 zero byte */
    911   char cnoncebuf[33];
    912   char *cnonce = NULL;
    913   size_t cnonce_sz = 0;
    914   char *userp_quoted;
    915   char *response = NULL;
    916   char *tmp = NULL;
    917 
    918   if(!digest->nc)
    919     digest->nc = 1;
    920 
    921   if(!digest->cnonce) {
    922     snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x",
    923              Curl_rand(data), Curl_rand(data),
    924              Curl_rand(data), Curl_rand(data));
    925 
    926     result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
    927                                 &cnonce, &cnonce_sz);
    928     if(result)
    929       return result;
    930 
    931     digest->cnonce = cnonce;
    932   }
    933 
    934   /*
    935     if the algorithm is "MD5" or unspecified (which then defaults to MD5):
    936 
    937     A1 = unq(username-value) ":" unq(realm-value) ":" passwd
    938 
    939     if the algorithm is "MD5-sess" then:
    940 
    941     A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
    942          ":" unq(nonce-value) ":" unq(cnonce-value)
    943   */
    944 
    945   md5this = (unsigned char *)
    946     aprintf("%s:%s:%s", userp, digest->realm, passwdp);
    947   if(!md5this)
    948     return CURLE_OUT_OF_MEMORY;
    949 
    950   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
    951   Curl_md5it(md5buf, md5this);
    952   free(md5this);
    953   sasl_digest_md5_to_ascii(md5buf, ha1);
    954 
    955   if(digest->algo == CURLDIGESTALGO_MD5SESS) {
    956     /* nonce and cnonce are OUTSIDE the hash */
    957     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
    958     if(!tmp)
    959       return CURLE_OUT_OF_MEMORY;
    960 
    961     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
    962     Curl_md5it(md5buf, (unsigned char *)tmp);
    963     free(tmp);
    964     sasl_digest_md5_to_ascii(md5buf, ha1);
    965   }
    966 
    967   /*
    968     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
    969 
    970       A2       = Method ":" digest-uri-value
    971 
    972           If the "qop" value is "auth-int", then A2 is:
    973 
    974       A2       = Method ":" digest-uri-value ":" H(entity-body)
    975 
    976     (The "Method" value is the HTTP request method as specified in section
    977     5.1.1 of RFC 2616)
    978   */
    979 
    980   md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
    981 
    982   if(digest->qop && Curl_raw_equal(digest->qop, "auth-int")) {
    983     /* We don't support auth-int for PUT or POST at the moment.
    984        TODO: replace md5 of empty string with entity-body for PUT/POST */
    985     unsigned char *md5this2 = (unsigned char *)
    986       aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
    987     free(md5this);
    988     md5this = md5this2;
    989   }
    990 
    991   if(!md5this)
    992     return CURLE_OUT_OF_MEMORY;
    993 
    994   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
    995   Curl_md5it(md5buf, md5this);
    996   free(md5this);
    997   sasl_digest_md5_to_ascii(md5buf, ha2);
    998 
    999   if(digest->qop) {
   1000     md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
   1001                                        ha1,
   1002                                        digest->nonce,
   1003                                        digest->nc,
   1004                                        digest->cnonce,
   1005                                        digest->qop,
   1006                                        ha2);
   1007   }
   1008   else {
   1009     md5this = (unsigned char *)aprintf("%s:%s:%s",
   1010                                        ha1,
   1011                                        digest->nonce,
   1012                                        ha2);
   1013   }
   1014 
   1015   if(!md5this)
   1016     return CURLE_OUT_OF_MEMORY;
   1017 
   1018   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
   1019   Curl_md5it(md5buf, md5this);
   1020   free(md5this);
   1021   sasl_digest_md5_to_ascii(md5buf, request_digest);
   1022 
   1023   /* for test case 64 (snooped from a Mozilla 1.3a request)
   1024 
   1025     Authorization: Digest username="testuser", realm="testrealm", \
   1026     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
   1027 
   1028     Digest parameters are all quoted strings.  Username which is provided by
   1029     the user will need double quotes and backslashes within it escaped.  For
   1030     the other fields, this shouldn't be an issue.  realm, nonce, and opaque
   1031     are copied as is from the server, escapes and all.  cnonce is generated
   1032     with web-safe characters.  uri is already percent encoded.  nc is 8 hex
   1033     characters.  algorithm and qop with standard values only contain web-safe
   1034     chracters.
   1035   */
   1036   userp_quoted = sasl_digest_string_quoted(userp);
   1037   if(!userp_quoted)
   1038     return CURLE_OUT_OF_MEMORY;
   1039 
   1040   if(digest->qop) {
   1041     response = aprintf("username=\"%s\", "
   1042                        "realm=\"%s\", "
   1043                        "nonce=\"%s\", "
   1044                        "uri=\"%s\", "
   1045                        "cnonce=\"%s\", "
   1046                        "nc=%08x, "
   1047                        "qop=%s, "
   1048                        "response=\"%s\"",
   1049                        userp_quoted,
   1050                        digest->realm,
   1051                        digest->nonce,
   1052                        uripath,
   1053                        digest->cnonce,
   1054                        digest->nc,
   1055                        digest->qop,
   1056                        request_digest);
   1057 
   1058     if(Curl_raw_equal(digest->qop, "auth"))
   1059       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
   1060                        padded which tells to the server how many times you are
   1061                        using the same nonce in the qop=auth mode */
   1062   }
   1063   else {
   1064     response = aprintf("username=\"%s\", "
   1065                        "realm=\"%s\", "
   1066                        "nonce=\"%s\", "
   1067                        "uri=\"%s\", "
   1068                        "response=\"%s\"",
   1069                        userp_quoted,
   1070                        digest->realm,
   1071                        digest->nonce,
   1072                        uripath,
   1073                        request_digest);
   1074   }
   1075   free(userp_quoted);
   1076   if(!response)
   1077     return CURLE_OUT_OF_MEMORY;
   1078 
   1079   /* Add the optional fields */
   1080   if(digest->opaque) {
   1081     /* Append the opaque */
   1082     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
   1083     free(response);
   1084     if(!tmp)
   1085       return CURLE_OUT_OF_MEMORY;
   1086 
   1087     response = tmp;
   1088   }
   1089 
   1090   if(digest->algorithm) {
   1091     /* Append the algorithm */
   1092     tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
   1093     free(response);
   1094     if(!tmp)
   1095       return CURLE_OUT_OF_MEMORY;
   1096 
   1097     response = tmp;
   1098   }
   1099 
   1100   /* Return the output */
   1101   *outptr = response;
   1102   *outlen = strlen(response);
   1103 
   1104   return CURLE_OK;
   1105 }
   1106 
   1107 /*
   1108  * Curl_sasl_digest_cleanup()
   1109  *
   1110  * This is used to clean up the digest specific data.
   1111  *
   1112  * Parameters:
   1113  *
   1114  * digest    [in/out] - The digest data struct being cleaned up.
   1115  *
   1116  */
   1117 void Curl_sasl_digest_cleanup(struct digestdata *digest)
   1118 {
   1119   Curl_safefree(digest->nonce);
   1120   Curl_safefree(digest->cnonce);
   1121   Curl_safefree(digest->realm);
   1122   Curl_safefree(digest->opaque);
   1123   Curl_safefree(digest->qop);
   1124   Curl_safefree(digest->algorithm);
   1125 
   1126   digest->nc = 0;
   1127   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
   1128   digest->stale = FALSE; /* default means normal, not stale */
   1129 }
   1130 #endif  /* !USE_WINDOWS_SSPI */
   1131 
   1132 #endif  /* CURL_DISABLE_CRYPTO_AUTH */
   1133 
   1134 #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
   1135 /*
   1136  * Curl_sasl_ntlm_cleanup()
   1137  *
   1138  * This is used to clean up the ntlm specific data.
   1139  *
   1140  * Parameters:
   1141  *
   1142  * ntlm    [in/out] - The ntlm data struct being cleaned up.
   1143  *
   1144  */
   1145 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
   1146 {
   1147   /* Free the target info */
   1148   Curl_safefree(ntlm->target_info);
   1149 
   1150   /* Reset any variables */
   1151   ntlm->target_info_len = 0;
   1152 }
   1153 #endif /* USE_NTLM && !USE_WINDOWS_SSPI*/
   1154 
   1155 /*
   1156  * sasl_create_xoauth2_message()
   1157  *
   1158  * This is used to generate an already encoded OAuth 2.0 message ready for
   1159  * sending to the recipient.
   1160  *
   1161  * Parameters:
   1162  *
   1163  * data    [in]     - The session handle.
   1164  * user    [in]     - The user name.
   1165  * bearer  [in]     - The bearer token.
   1166  * outptr  [in/out] - The address where a pointer to newly allocated memory
   1167  *                    holding the result will be stored upon completion.
   1168  * outlen  [out]    - The length of the output message.
   1169  *
   1170  * Returns CURLE_OK on success.
   1171  */
   1172 static CURLcode sasl_create_xoauth2_message(struct SessionHandle *data,
   1173                                             const char *user,
   1174                                             const char *bearer,
   1175                                             char **outptr, size_t *outlen)
   1176 {
   1177   CURLcode result = CURLE_OK;
   1178   char *xoauth = NULL;
   1179 
   1180   /* Generate the message */
   1181   xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
   1182   if(!xoauth)
   1183     return CURLE_OUT_OF_MEMORY;
   1184 
   1185   /* Base64 encode the reply */
   1186   result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen);
   1187 
   1188   free(xoauth);
   1189 
   1190   return result;
   1191 }
   1192 
   1193 /*
   1194  * Curl_sasl_cleanup()
   1195  *
   1196  * This is used to cleanup any libraries or curl modules used by the sasl
   1197  * functions.
   1198  *
   1199  * Parameters:
   1200  *
   1201  * conn     [in]     - The connection data.
   1202  * authused [in]     - The authentication mechanism used.
   1203  */
   1204 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
   1205 {
   1206 #if defined(USE_KERBEROS5)
   1207   /* Cleanup the gssapi structure */
   1208   if(authused == SASL_MECH_GSSAPI) {
   1209     Curl_sasl_gssapi_cleanup(&conn->krb5);
   1210   }
   1211 #endif
   1212 
   1213 #if defined(USE_NTLM)
   1214   /* Cleanup the ntlm structure */
   1215   if(authused == SASL_MECH_NTLM) {
   1216     Curl_sasl_ntlm_cleanup(&conn->ntlm);
   1217   }
   1218 #endif
   1219 
   1220 #if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
   1221   /* Reserved for future use */
   1222   (void)conn;
   1223   (void)authused;
   1224 #endif
   1225 }
   1226 
   1227 /*
   1228  * Curl_sasl_decode_mech()
   1229  *
   1230  * Convert a SASL mechanism name into a token.
   1231  *
   1232  * Parameters:
   1233  *
   1234  * ptr    [in]     - The mechanism string.
   1235  * maxlen [in]     - Maximum mechanism string length.
   1236  * len    [out]    - If not NULL, effective name length.
   1237  *
   1238  * Returns the SASL mechanism token or 0 if no match.
   1239  */
   1240 unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
   1241 {
   1242   unsigned int i;
   1243   char c;
   1244 
   1245   for(i = 0; mechtable[i].name; i++) {
   1246     if(maxlen >= mechtable[i].len &&
   1247        !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
   1248       if(len)
   1249         *len = mechtable[i].len;
   1250 
   1251       if(maxlen == mechtable[i].len)
   1252         return mechtable[i].bit;
   1253 
   1254       c = ptr[mechtable[i].len];
   1255       if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
   1256         return mechtable[i].bit;
   1257     }
   1258   }
   1259 
   1260   return 0;
   1261 }
   1262 
   1263 /*
   1264  * Curl_sasl_parse_url_auth_option()
   1265  *
   1266  * Parse the URL login options.
   1267  */
   1268 CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
   1269                                          const char *value, size_t len)
   1270 {
   1271   CURLcode result = CURLE_OK;
   1272   unsigned int mechbit;
   1273   size_t mechlen;
   1274 
   1275   if(!len)
   1276     return CURLE_URL_MALFORMAT;
   1277 
   1278     if(sasl->resetprefs) {
   1279       sasl->resetprefs = FALSE;
   1280       sasl->prefmech = SASL_AUTH_NONE;
   1281     }
   1282 
   1283     if(strnequal(value, "*", len))
   1284       sasl->prefmech = SASL_AUTH_DEFAULT;
   1285     else if((mechbit = Curl_sasl_decode_mech(value, len, &mechlen)) &&
   1286             mechlen == len)
   1287       sasl->prefmech |= mechbit;
   1288     else
   1289       result = CURLE_URL_MALFORMAT;
   1290 
   1291   return result;
   1292 }
   1293 
   1294 /*
   1295  * Curl_sasl_init()
   1296  *
   1297  * Initializes the SASL structure.
   1298  */
   1299 void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
   1300 {
   1301   sasl->params = params;           /* Set protocol dependent parameters */
   1302   sasl->state = SASL_STOP;         /* Not yet running */
   1303   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
   1304   sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
   1305   sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
   1306   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
   1307   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
   1308   sasl->force_ir = FALSE;          /* Respect external option */
   1309 }
   1310 
   1311 /*
   1312  * state()
   1313  *
   1314  * This is the ONLY way to change SASL state!
   1315  */
   1316 static void state(struct SASL *sasl, struct connectdata *conn,
   1317                   saslstate newstate)
   1318 {
   1319 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   1320   /* for debug purposes */
   1321   static const char * const names[]={
   1322     "STOP",
   1323     "PLAIN",
   1324     "LOGIN",
   1325     "LOGIN_PASSWD",
   1326     "EXTERNAL",
   1327     "CRAMMD5",
   1328     "DIGESTMD5",
   1329     "DIGESTMD5_RESP",
   1330     "NTLM",
   1331     "NTLM_TYPE2MSG",
   1332     "GSSAPI",
   1333     "GSSAPI_TOKEN",
   1334     "GSSAPI_NO_DATA",
   1335     "XOAUTH2",
   1336     "CANCEL",
   1337     "FINAL",
   1338     /* LAST */
   1339   };
   1340 
   1341   if(sasl->state != newstate)
   1342     infof(conn->data, "SASL %p state change from %s to %s\n",
   1343           (void *)sasl, names[sasl->state], names[newstate]);
   1344 #else
   1345   (void) conn;
   1346 #endif
   1347 
   1348   sasl->state = newstate;
   1349 }
   1350 
   1351 /*
   1352  * Curl_sasl_can_authenticate()
   1353  *
   1354  * Check if we have enough auth data and capabilities to authenticate.
   1355  */
   1356 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
   1357 {
   1358   /* Have credentials been provided? */
   1359   if(conn->bits.user_passwd)
   1360     return TRUE;
   1361 
   1362   /* EXTERNAL can authenticate without a user name and/or password */
   1363   if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
   1364     return TRUE;
   1365 
   1366   return FALSE;
   1367 }
   1368 
   1369 /*
   1370  * Curl_sasl_start()
   1371  *
   1372  * Calculate the required login details for SASL authentication.
   1373  */
   1374 CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
   1375                          bool force_ir, saslprogress *progress)
   1376 {
   1377   CURLcode result = CURLE_OK;
   1378   struct SessionHandle *data = conn->data;
   1379   unsigned int enabledmechs;
   1380   const char *mech = NULL;
   1381   char *resp = NULL;
   1382   size_t len = 0;
   1383   saslstate state1 = SASL_STOP;
   1384   saslstate state2 = SASL_FINAL;
   1385 
   1386   sasl->force_ir = force_ir;    /* Latch for future use */
   1387   sasl->authused = 0;           /* No mechanism used yet */
   1388   enabledmechs = sasl->authmechs & sasl->prefmech;
   1389   *progress = SASL_IDLE;
   1390 
   1391   /* Calculate the supported authentication mechanism, by decreasing order of
   1392      security, as well as the initial response where appropriate */
   1393   if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
   1394     mech = SASL_MECH_STRING_EXTERNAL;
   1395     state1 = SASL_EXTERNAL;
   1396     sasl->authused = SASL_MECH_EXTERNAL;
   1397 
   1398     if(force_ir || data->set.sasl_ir)
   1399       result = sasl_create_external_message(data, conn->user, &resp, &len);
   1400   }
   1401   else if(conn->bits.user_passwd) {
   1402 #if defined(USE_KERBEROS5)
   1403     if(enabledmechs & SASL_MECH_GSSAPI) {
   1404       sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
   1405       mech = SASL_MECH_STRING_GSSAPI;
   1406       state1 = SASL_GSSAPI;
   1407       state2 = SASL_GSSAPI_TOKEN;
   1408       sasl->authused = SASL_MECH_GSSAPI;
   1409 
   1410       if(force_ir || data->set.sasl_ir)
   1411         result = Curl_sasl_create_gssapi_user_message(data, conn->user,
   1412                                                       conn->passwd,
   1413                                                       sasl->params->service,
   1414                                                       sasl->mutual_auth,
   1415                                                       NULL, &conn->krb5,
   1416                                                       &resp, &len);
   1417     }
   1418     else
   1419 #endif
   1420 #ifndef CURL_DISABLE_CRYPTO_AUTH
   1421     if(enabledmechs & SASL_MECH_DIGEST_MD5) {
   1422       mech = SASL_MECH_STRING_DIGEST_MD5;
   1423       state1 = SASL_DIGESTMD5;
   1424       sasl->authused = SASL_MECH_DIGEST_MD5;
   1425     }
   1426     else if(enabledmechs & SASL_MECH_CRAM_MD5) {
   1427       mech = SASL_MECH_STRING_CRAM_MD5;
   1428       state1 = SASL_CRAMMD5;
   1429       sasl->authused = SASL_MECH_CRAM_MD5;
   1430     }
   1431     else
   1432 #endif
   1433 #ifdef USE_NTLM
   1434     if(enabledmechs & SASL_MECH_NTLM) {
   1435       mech = SASL_MECH_STRING_NTLM;
   1436       state1 = SASL_NTLM;
   1437       state2 = SASL_NTLM_TYPE2MSG;
   1438       sasl->authused = SASL_MECH_NTLM;
   1439 
   1440       if(force_ir || data->set.sasl_ir)
   1441         result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
   1442                                                      &conn->ntlm, &resp, &len);
   1443       }
   1444     else
   1445 #endif
   1446     if((enabledmechs & SASL_MECH_XOAUTH2) || conn->xoauth2_bearer) {
   1447       mech = SASL_MECH_STRING_XOAUTH2;
   1448       state1 = SASL_XOAUTH2;
   1449       sasl->authused = SASL_MECH_XOAUTH2;
   1450 
   1451       if(force_ir || data->set.sasl_ir)
   1452         result = sasl_create_xoauth2_message(data, conn->user,
   1453                                              conn->xoauth2_bearer,
   1454                                              &resp, &len);
   1455     }
   1456     else if(enabledmechs & SASL_MECH_LOGIN) {
   1457       mech = SASL_MECH_STRING_LOGIN;
   1458       state1 = SASL_LOGIN;
   1459       state2 = SASL_LOGIN_PASSWD;
   1460       sasl->authused = SASL_MECH_LOGIN;
   1461 
   1462       if(force_ir || data->set.sasl_ir)
   1463         result = sasl_create_login_message(data, conn->user, &resp, &len);
   1464     }
   1465     else if(enabledmechs & SASL_MECH_PLAIN) {
   1466       mech = SASL_MECH_STRING_PLAIN;
   1467       state1 = SASL_PLAIN;
   1468       sasl->authused = SASL_MECH_PLAIN;
   1469 
   1470       if(force_ir || data->set.sasl_ir)
   1471         result = sasl_create_plain_message(data, conn->user, conn->passwd,
   1472                                            &resp, &len);
   1473     }
   1474   }
   1475 
   1476   if(!result) {
   1477     if(resp && sasl->params->maxirlen &&
   1478        strlen(mech) + len > sasl->params->maxirlen) {
   1479       free(resp);
   1480       resp = NULL;
   1481     }
   1482 
   1483     if(mech) {
   1484       result = sasl->params->sendauth(conn, mech, resp);
   1485       if(!result) {
   1486         *progress = SASL_INPROGRESS;
   1487         state(sasl, conn, resp? state2: state1);
   1488       }
   1489     }
   1490   }
   1491 
   1492   free(resp);
   1493 
   1494   return result;
   1495 }
   1496 
   1497 /*
   1498  * Curl_sasl_continue()
   1499  *
   1500  * Continue the authentication.
   1501  */
   1502 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
   1503                             int code, saslprogress *progress)
   1504 {
   1505   CURLcode result = CURLE_OK;
   1506   struct SessionHandle *data = conn->data;
   1507   saslstate newstate = SASL_FINAL;
   1508   char *resp = NULL;
   1509 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
   1510   char *serverdata;
   1511   char *chlg = NULL;
   1512   size_t chlglen = 0;
   1513 #endif
   1514   size_t len = 0;
   1515 
   1516   *progress = SASL_INPROGRESS;
   1517 
   1518   if(sasl->state == SASL_FINAL) {
   1519     if(code != sasl->params->finalcode)
   1520       result = CURLE_LOGIN_DENIED;
   1521     *progress = SASL_DONE;
   1522     state(sasl, conn, SASL_STOP);
   1523     return result;
   1524   }
   1525 
   1526   if(sasl->state != SASL_CANCEL && code != sasl->params->contcode) {
   1527     *progress = SASL_DONE;
   1528     state(sasl, conn, SASL_STOP);
   1529     return CURLE_LOGIN_DENIED;
   1530   }
   1531 
   1532   switch(sasl->state) {
   1533   case SASL_STOP:
   1534     *progress = SASL_DONE;
   1535     return result;
   1536   case SASL_PLAIN:
   1537     result = sasl_create_plain_message(data, conn->user, conn->passwd, &resp,
   1538                                        &len);
   1539     break;
   1540   case SASL_LOGIN:
   1541     result = sasl_create_login_message(data, conn->user, &resp, &len);
   1542     newstate = SASL_LOGIN_PASSWD;
   1543     break;
   1544   case SASL_LOGIN_PASSWD:
   1545     result = sasl_create_login_message(data, conn->passwd, &resp, &len);
   1546     break;
   1547   case SASL_EXTERNAL:
   1548     result = sasl_create_external_message(data, conn->user, &resp, &len);
   1549     break;
   1550 
   1551 #ifndef CURL_DISABLE_CRYPTO_AUTH
   1552   case SASL_CRAMMD5:
   1553     sasl->params->getmessage(data->state.buffer, &serverdata);
   1554     result = sasl_decode_cram_md5_message(serverdata, &chlg, &chlglen);
   1555     if(!result)
   1556       result = sasl_create_cram_md5_message(data, chlg, conn->user,
   1557                                             conn->passwd, &resp, &len);
   1558     free(chlg);
   1559     break;
   1560   case SASL_DIGESTMD5:
   1561     sasl->params->getmessage(data->state.buffer, &serverdata);
   1562     result = Curl_sasl_create_digest_md5_message(data, serverdata,
   1563                                                  conn->user, conn->passwd,
   1564                                                  sasl->params->service,
   1565                                                  &resp, &len);
   1566     newstate = SASL_DIGESTMD5_RESP;
   1567     break;
   1568   case SASL_DIGESTMD5_RESP:
   1569     if(!(resp = strdup("")))
   1570       result = CURLE_OUT_OF_MEMORY;
   1571     break;
   1572 #endif
   1573 
   1574 #ifdef USE_NTLM
   1575   case SASL_NTLM:
   1576     /* Create the type-1 message */
   1577     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
   1578                                                  &conn->ntlm, &resp, &len);
   1579     newstate = SASL_NTLM_TYPE2MSG;
   1580     break;
   1581   case SASL_NTLM_TYPE2MSG:
   1582     /* Decode the type-2 message */
   1583     sasl->params->getmessage(data->state.buffer, &serverdata);
   1584     result = Curl_sasl_decode_ntlm_type2_message(data, serverdata,
   1585                                                  &conn->ntlm);
   1586     if(!result)
   1587       result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
   1588                                                    conn->passwd, &conn->ntlm,
   1589                                                    &resp, &len);
   1590     break;
   1591 #endif
   1592 
   1593 #if defined(USE_KERBEROS5)
   1594   case SASL_GSSAPI:
   1595     result = Curl_sasl_create_gssapi_user_message(data, conn->user,
   1596                                                   conn->passwd,
   1597                                                   sasl->params->service,
   1598                                                   sasl->mutual_auth, NULL,
   1599                                                   &conn->krb5,
   1600                                                   &resp, &len);
   1601     newstate = SASL_GSSAPI_TOKEN;
   1602     break;
   1603   case SASL_GSSAPI_TOKEN:
   1604     sasl->params->getmessage(data->state.buffer, &serverdata);
   1605     if(sasl->mutual_auth) {
   1606       /* Decode the user token challenge and create the optional response
   1607          message */
   1608       result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
   1609                                                     sasl->mutual_auth,
   1610                                                     serverdata, &conn->krb5,
   1611                                                     &resp, &len);
   1612       newstate = SASL_GSSAPI_NO_DATA;
   1613     }
   1614     else
   1615       /* Decode the security challenge and create the response message */
   1616       result = Curl_sasl_create_gssapi_security_message(data, serverdata,
   1617                                                         &conn->krb5,
   1618                                                         &resp, &len);
   1619     break;
   1620   case SASL_GSSAPI_NO_DATA:
   1621     sasl->params->getmessage(data->state.buffer, &serverdata);
   1622     /* Decode the security challenge and create the response message */
   1623     result = Curl_sasl_create_gssapi_security_message(data, serverdata,
   1624                                                       &conn->krb5,
   1625                                                       &resp, &len);
   1626     break;
   1627 #endif
   1628 
   1629   case SASL_XOAUTH2:
   1630     /* Create the authorisation message */
   1631     result = sasl_create_xoauth2_message(data, conn->user,
   1632                                          conn->xoauth2_bearer, &resp, &len);
   1633     break;
   1634   case SASL_CANCEL:
   1635     /* Remove the offending mechanism from the supported list */
   1636     sasl->authmechs ^= sasl->authused;
   1637 
   1638     /* Start an alternative SASL authentication */
   1639     result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
   1640     newstate = sasl->state;   /* Use state from Curl_sasl_start() */
   1641     break;
   1642   default:
   1643     failf(data, "Unsupported SASL authentication mechanism");
   1644     result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
   1645     break;
   1646   }
   1647 
   1648   switch(result) {
   1649   case CURLE_BAD_CONTENT_ENCODING:
   1650     /* Cancel dialog */
   1651     result = sasl->params->sendcont(conn, "*");
   1652     newstate = SASL_CANCEL;
   1653     break;
   1654   case CURLE_OK:
   1655     if(resp)
   1656       result = sasl->params->sendcont(conn, resp);
   1657     break;
   1658   default:
   1659     newstate = SASL_STOP;    /* Stop on error */
   1660     *progress = SASL_DONE;
   1661     break;
   1662   }
   1663 
   1664   free(resp);
   1665 
   1666   state(sasl, conn, newstate);
   1667 
   1668   return result;
   1669 }
   1670