1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2015, 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 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  */
Curl_sasl_digest_get_pair(const char * str,char * value,char * content,const char ** endptr)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*/
sasl_digest_md5_to_ascii(unsigned char * source,unsigned char * dest)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 */
sasl_digest_string_quoted(const char * source)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  */
sasl_digest_get_key_value(const char * chlg,const char * key,char * value,size_t max_val_len,char end_char)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 
sasl_digest_get_qop_values(const char * options,int * value)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  */
Curl_sasl_build_spn(const char * service,const char * host)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  */
sasl_create_plain_message(struct SessionHandle * data,const char * userp,const char * passwdp,char ** outptr,size_t * outlen)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  */
sasl_create_login_message(struct SessionHandle * data,const char * valuep,char ** outptr,size_t * outlen)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  */
sasl_create_external_message(struct SessionHandle * data,const char * user,char ** outptr,size_t * outlen)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  */
sasl_decode_cram_md5_message(const char * chlg64,char ** outptr,size_t * outlen)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  */
sasl_create_cram_md5_message(struct SessionHandle * data,const char * chlg,const char * userp,const char * passwdp,char ** outptr,size_t * outlen)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  */
sasl_decode_digest_md5_message(const char * chlg64,char * nonce,size_t nlen,char * realm,size_t rlen,char * alg,size_t alen,char * qop,size_t qlen)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  */
Curl_sasl_create_digest_md5_message(struct SessionHandle * data,const char * chlg64,const char * userp,const char * passwdp,const char * service,char ** outptr,size_t * outlen)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  */
Curl_sasl_decode_digest_http_message(const char * chlg,struct digestdata * digest)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  */
Curl_sasl_create_digest_http_message(struct SessionHandle * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen)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  */
Curl_sasl_digest_cleanup(struct digestdata * digest)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  */
Curl_sasl_ntlm_cleanup(struct ntlmdata * ntlm)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  */
sasl_create_xoauth2_message(struct SessionHandle * data,const char * user,const char * bearer,char ** outptr,size_t * outlen)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  */
Curl_sasl_cleanup(struct connectdata * conn,unsigned int authused)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  */
Curl_sasl_decode_mech(const char * ptr,size_t maxlen,size_t * len)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  */
Curl_sasl_parse_url_auth_option(struct SASL * sasl,const char * value,size_t len)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  */
Curl_sasl_init(struct SASL * sasl,const struct SASLproto * params)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  */
state(struct SASL * sasl,struct connectdata * conn,saslstate newstate)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  */
Curl_sasl_can_authenticate(struct SASL * sasl,struct connectdata * conn)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  */
Curl_sasl_start(struct SASL * sasl,struct connectdata * conn,bool force_ir,saslprogress * progress)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  */
Curl_sasl_continue(struct SASL * sasl,struct connectdata * conn,int code,saslprogress * progress)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