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