tizen 2.3.1 release
[external/curl.git] / lib / curl_sasl.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2014, 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 "curl_memory.h"
43 #include "strtok.h"
44 #include "rawstr.h"
45 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
46
47 #define _MPRINTF_REPLACE /* use our functions only */
48 #include <curl/mprintf.h>
49
50 /* The last #include file should be: */
51 #include "memdebug.h"
52
53 #if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI)
54 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
55 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
56 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
57
58 #define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
59 #define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
60 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
61
62 #define DIGEST_MAX_VALUE_LENGTH           256
63 #define DIGEST_MAX_CONTENT_LENGTH         1024
64
65 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
66    It converts digest text to ASCII so the MD5 will be correct for
67    what ultimately goes over the network.
68 */
69 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
70   result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
71   if(result) { \
72     free(b); \
73     return result; \
74   }
75
76 /*
77  * Return 0 on success and then the buffers are filled in fine.
78  *
79  * Non-zero means failure to parse.
80  */
81 static int sasl_digest_get_pair(const char *str, char *value, char *content,
82                                 const char **endptr)
83 {
84   int c;
85   bool starts_with_quote = FALSE;
86   bool escape = FALSE;
87
88   for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--); )
89     *value++ = *str++;
90   *value = 0;
91
92   if('=' != *str++)
93     /* eek, no match */
94     return 1;
95
96   if('\"' == *str) {
97     /* this starts with a quote so it must end with one as well! */
98     str++;
99     starts_with_quote = TRUE;
100   }
101
102   for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
103     switch(*str) {
104     case '\\':
105       if(!escape) {
106         /* possibly the start of an escaped quote */
107         escape = TRUE;
108         *content++ = '\\'; /* even though this is an escape character, we still
109                               store it as-is in the target buffer */
110         continue;
111       }
112       break;
113     case ',':
114       if(!starts_with_quote) {
115         /* this signals the end of the content if we didn't get a starting
116            quote and then we do "sloppy" parsing */
117         c = 0; /* the end */
118         continue;
119       }
120       break;
121     case '\r':
122     case '\n':
123       /* end of string */
124       c = 0;
125       continue;
126     case '\"':
127       if(!escape && starts_with_quote) {
128         /* end of string */
129         c = 0;
130         continue;
131       }
132       break;
133     }
134     escape = FALSE;
135     *content++ = *str;
136   }
137   *content = 0;
138
139   *endptr = str;
140
141   return 0; /* all is fine! */
142 }
143
144 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
145 static void sasl_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
146                                      unsigned char *dest) /* 33 bytes */
147 {
148   int i;
149   for(i = 0; i < 16; i++)
150     snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
151 }
152
153 /* Perform quoted-string escaping as described in RFC2616 and its errata */
154 static char *sasl_digest_string_quoted(const char *source)
155 {
156   char *dest, *d;
157   const char *s = source;
158   size_t n = 1; /* null terminator */
159
160   /* Calculate size needed */
161   while(*s) {
162     ++n;
163     if(*s == '"' || *s == '\\') {
164       ++n;
165     }
166     ++s;
167   }
168
169   dest = malloc(n);
170   if(dest) {
171     s = source;
172     d = dest;
173     while(*s) {
174       if(*s == '"' || *s == '\\') {
175         *d++ = '\\';
176       }
177       *d++ = *s++;
178     }
179     *d = 0;
180   }
181
182   return dest;
183 }
184
185 /* Retrieves the value for a corresponding key from the challenge string
186  * returns TRUE if the key could be found, FALSE if it does not exists
187  */
188 static bool sasl_digest_get_key_value(const char *chlg,
189                                       const char *key,
190                                       char *value,
191                                       size_t max_val_len,
192                                       char end_char)
193 {
194   char *find_pos;
195   size_t i;
196
197   find_pos = strstr(chlg, key);
198   if(!find_pos)
199     return FALSE;
200
201   find_pos += strlen(key);
202
203   for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
204     value[i] = *find_pos++;
205   value[i] = '\0';
206
207   return TRUE;
208 }
209
210 static CURLcode sasl_digest_get_qop_values(const char *options, int *value)
211 {
212   char *tmp;
213   char *token;
214   char *tok_buf;
215
216   /* Initialise the output */
217   *value = 0;
218
219   /* Tokenise the list of qop values. Use a temporary clone of the buffer since
220      strtok_r() ruins it. */
221   tmp = strdup(options);
222   if(!tmp)
223     return CURLE_OUT_OF_MEMORY;
224
225   token = strtok_r(tmp, ",", &tok_buf);
226   while(token != NULL) {
227     if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH))
228       *value |= DIGEST_QOP_VALUE_AUTH;
229     else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
230       *value |= DIGEST_QOP_VALUE_AUTH_INT;
231     else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
232       *value |= DIGEST_QOP_VALUE_AUTH_CONF;
233
234     token = strtok_r(NULL, ",", &tok_buf);
235   }
236
237   Curl_safefree(tmp);
238
239   return CURLE_OK;
240 }
241 #endif /* !CURL_DISABLE_CRYPTO_AUTH && !USE_WINDOWS_SSPI */
242
243 #if !defined(USE_WINDOWS_SSPI)
244 /*
245  * Curl_sasl_build_spn()
246  *
247  * This is used to build a SPN string in the format service/host.
248  *
249  * Parameters:
250  *
251  * serivce  [in] - The service type such as www, smtp, pop or imap.
252  * host     [in] - The host name or realm.
253  *
254  * Returns a pointer to the newly allocated SPN.
255  */
256 char *Curl_sasl_build_spn(const char *service, const char *host)
257 {
258   /* Generate and return our SPN */
259   return aprintf("%s/%s", service, host);
260 }
261 #endif
262
263 /*
264  * Curl_sasl_create_plain_message()
265  *
266  * This is used to generate an already encoded PLAIN message ready
267  * for sending to the recipient.
268  *
269  * Parameters:
270  *
271  * data    [in]     - The session handle.
272  * userp   [in]     - The user name.
273  * passdwp [in]     - The user's password.
274  * outptr  [in/out] - The address where a pointer to newly allocated memory
275  *                    holding the result will be stored upon completion.
276  * outlen  [out]    - The length of the output message.
277  *
278  * Returns CURLE_OK on success.
279  */
280 CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data,
281                                         const char *userp,
282                                         const char *passwdp,
283                                         char **outptr, size_t *outlen)
284 {
285   CURLcode result;
286   char *plainauth;
287   size_t ulen;
288   size_t plen;
289
290   ulen = strlen(userp);
291   plen = strlen(passwdp);
292
293   plainauth = malloc(2 * ulen + plen + 2);
294   if(!plainauth) {
295     *outlen = 0;
296     *outptr = NULL;
297     return CURLE_OUT_OF_MEMORY;
298   }
299
300   /* Calculate the reply */
301   memcpy(plainauth, userp, ulen);
302   plainauth[ulen] = '\0';
303   memcpy(plainauth + ulen + 1, userp, ulen);
304   plainauth[2 * ulen + 1] = '\0';
305   memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
306
307   /* Base64 encode the reply */
308   result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr,
309                               outlen);
310   Curl_safefree(plainauth);
311   return result;
312 }
313
314 /*
315  * Curl_sasl_create_login_message()
316  *
317  * This is used to generate an already encoded LOGIN message containing the
318  * user name or password ready for sending to the recipient.
319  *
320  * Parameters:
321  *
322  * data    [in]     - The session handle.
323  * valuep  [in]     - The user name or user's password.
324  * outptr  [in/out] - The address where a pointer to newly allocated memory
325  *                    holding the result will be stored upon completion.
326  * outlen  [out]    - The length of the output message.
327  *
328  * Returns CURLE_OK on success.
329  */
330 CURLcode Curl_sasl_create_login_message(struct SessionHandle *data,
331                                         const char *valuep, char **outptr,
332                                         size_t *outlen)
333 {
334   size_t vlen = strlen(valuep);
335
336   if(!vlen) {
337     /* Calculate an empty reply */
338     *outptr = strdup("=");
339     if(*outptr) {
340       *outlen = (size_t) 1;
341       return CURLE_OK;
342     }
343
344     *outlen = 0;
345     return CURLE_OUT_OF_MEMORY;
346   }
347
348   /* Base64 encode the value */
349   return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
350 }
351
352 #ifndef CURL_DISABLE_CRYPTO_AUTH
353  /*
354  * Curl_sasl_decode_cram_md5_message()
355  *
356  * This is used to decode an already encoded CRAM-MD5 challenge message.
357  *
358  * Parameters:
359  *
360  * chlg64  [in]     - The base64 encoded challenge message.
361  * outptr  [in/out] - The address where a pointer to newly allocated memory
362  *                    holding the result will be stored upon completion.
363  * outlen  [out]    - The length of the output message.
364  *
365  * Returns CURLE_OK on success.
366  */
367 CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr,
368                                            size_t *outlen)
369 {
370   CURLcode result = CURLE_OK;
371   size_t chlg64len = strlen(chlg64);
372
373   *outptr = NULL;
374   *outlen = 0;
375
376   /* Decode the challenge if necessary */
377   if(chlg64len && *chlg64 != '=')
378     result = Curl_base64_decode(chlg64, (unsigned char **) outptr, outlen);
379
380     return result;
381  }
382
383  /*
384  * Curl_sasl_create_cram_md5_message()
385  *
386  * This is used to generate an already encoded CRAM-MD5 response message ready
387  * for sending to the recipient.
388  *
389  * Parameters:
390  *
391  * data    [in]     - The session handle.
392  * chlg    [in]     - The challenge.
393  * userp   [in]     - The user name.
394  * passdwp [in]     - The user's password.
395  * outptr  [in/out] - The address where a pointer to newly allocated memory
396  *                    holding the result will be stored upon completion.
397  * outlen  [out]    - The length of the output message.
398  *
399  * Returns CURLE_OK on success.
400  */
401 CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data,
402                                            const char *chlg,
403                                            const char *userp,
404                                            const char *passwdp,
405                                            char **outptr, size_t *outlen)
406 {
407   CURLcode result = CURLE_OK;
408   size_t chlglen = 0;
409   HMAC_context *ctxt;
410   unsigned char digest[MD5_DIGEST_LEN];
411   char *response;
412
413   if(chlg)
414     chlglen = strlen(chlg);
415
416   /* Compute the digest using the password as the key */
417   ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
418                         (const unsigned char *) passwdp,
419                         curlx_uztoui(strlen(passwdp)));
420   if(!ctxt)
421     return CURLE_OUT_OF_MEMORY;
422
423   /* Update the digest with the given challenge */
424   if(chlglen > 0)
425     Curl_HMAC_update(ctxt, (const unsigned char *) chlg,
426                      curlx_uztoui(chlglen));
427
428   /* Finalise the digest */
429   Curl_HMAC_final(ctxt, digest);
430
431   /* Generate the response */
432   response = aprintf(
433       "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
434            userp, digest[0], digest[1], digest[2], digest[3], digest[4],
435            digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
436            digest[11], digest[12], digest[13], digest[14], digest[15]);
437   if(!response)
438     return CURLE_OUT_OF_MEMORY;
439
440   /* Base64 encode the response */
441   result = Curl_base64_encode(data, response, 0, outptr, outlen);
442
443   Curl_safefree(response);
444
445   return result;
446 }
447
448 #ifndef USE_WINDOWS_SSPI
449 /*
450  * sasl_decode_digest_md5_message()
451  *
452  * This is used internally to decode an already encoded DIGEST-MD5 challenge
453  * message into the seperate attributes.
454  *
455  * Parameters:
456  *
457  * chlg64  [in]     - The base64 encoded challenge message.
458  * nonce   [in/out] - The buffer where the nonce will be stored.
459  * nlen    [in]     - The length of the nonce buffer.
460  * realm   [in/out] - The buffer where the realm will be stored.
461  * rlen    [in]     - The length of the realm buffer.
462  * alg     [in/out] - The buffer where the algorithm will be stored.
463  * alen    [in]     - The length of the algorithm buffer.
464  * qop     [in/out] - The buffer where the qop-options will be stored.
465  * qlen    [in]     - The length of the qop buffer.
466  *
467  * Returns CURLE_OK on success.
468  */
469 static CURLcode sasl_decode_digest_md5_message(const char *chlg64,
470                                                char *nonce, size_t nlen,
471                                                char *realm, size_t rlen,
472                                                char *alg, size_t alen,
473                                                char *qop, size_t qlen)
474 {
475   CURLcode result = CURLE_OK;
476   unsigned char *chlg = NULL;
477   size_t chlglen = 0;
478   size_t chlg64len = strlen(chlg64);
479
480   /* Decode the base-64 encoded challenge message */
481   if(chlg64len && *chlg64 != '=') {
482     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
483     if(result)
484       return result;
485   }
486
487   /* Ensure we have a valid challenge message */
488   if(!chlg)
489     return CURLE_BAD_CONTENT_ENCODING;
490
491   /* Retrieve nonce string from the challenge */
492   if(!sasl_digest_get_key_value((char *)chlg, "nonce=\"", nonce, nlen, '\"')) {
493     Curl_safefree(chlg);
494     return CURLE_BAD_CONTENT_ENCODING;
495   }
496
497   /* Retrieve realm string from the challenge */
498   if(!sasl_digest_get_key_value((char *)chlg, "realm=\"", realm, rlen, '\"')) {
499     /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
500     strcpy(realm, "");
501   }
502
503   /* Retrieve algorithm string from the challenge */
504   if(!sasl_digest_get_key_value((char *)chlg, "algorithm=", alg, alen, ',')) {
505     Curl_safefree(chlg);
506     return CURLE_BAD_CONTENT_ENCODING;
507   }
508
509   /* Retrieve qop-options string from the challenge */
510   if(!sasl_digest_get_key_value((char *)chlg, "qop=\"", qop, qlen, '\"')) {
511     Curl_safefree(chlg);
512     return CURLE_BAD_CONTENT_ENCODING;
513   }
514
515   Curl_safefree(chlg);
516
517   return CURLE_OK;
518 }
519
520 /*
521  * Curl_sasl_create_digest_md5_message()
522  *
523  * This is used to generate an already encoded DIGEST-MD5 response message
524  * ready for sending to the recipient.
525  *
526  * Parameters:
527  *
528  * data    [in]     - The session handle.
529  * chlg64  [in]     - The base64 encoded challenge message.
530  * userp   [in]     - The user name.
531  * passdwp [in]     - The user's password.
532  * service [in]     - The service type such as www, smtp, pop or imap.
533  * outptr  [in/out] - The address where a pointer to newly allocated memory
534  *                    holding the result will be stored upon completion.
535  * outlen  [out]    - The length of the output message.
536  *
537  * Returns CURLE_OK on success.
538  */
539 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
540                                              const char *chlg64,
541                                              const char *userp,
542                                              const char *passwdp,
543                                              const char *service,
544                                              char **outptr, size_t *outlen)
545 {
546   CURLcode result = CURLE_OK;
547   size_t i;
548   MD5_context *ctxt;
549   char *response = NULL;
550   unsigned char digest[MD5_DIGEST_LEN];
551   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
552   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
553   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
554   char nonce[64];
555   char realm[128];
556   char algorithm[64];
557   char qop_options[64];
558   int qop_values;
559   char cnonce[33];
560   unsigned int entropy[4];
561   char nonceCount[] = "00000001";
562   char method[]     = "AUTHENTICATE";
563   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
564   char *spn         = NULL;
565
566   /* Decode the challange message */
567   result = sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
568                                           realm, sizeof(realm),
569                                           algorithm, sizeof(algorithm),
570                                           qop_options, sizeof(qop_options));
571   if(result)
572     return result;
573
574   /* We only support md5 sessions */
575   if(strcmp(algorithm, "md5-sess") != 0)
576     return CURLE_BAD_CONTENT_ENCODING;
577
578   /* Get the qop-values from the qop-options */
579   result = sasl_digest_get_qop_values(qop_options, &qop_values);
580   if(result)
581     return result;
582
583   /* We only support auth quality-of-protection */
584   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
585     return CURLE_BAD_CONTENT_ENCODING;
586
587   /* Generate 16 bytes of random data */
588   entropy[0] = Curl_rand(data);
589   entropy[1] = Curl_rand(data);
590   entropy[2] = Curl_rand(data);
591   entropy[3] = Curl_rand(data);
592
593   /* Convert the random data into a 32 byte hex string */
594   snprintf(cnonce, sizeof(cnonce), "%08x%08x%08x%08x",
595            entropy[0], entropy[1], entropy[2], entropy[3]);
596
597   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
598   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
599   if(!ctxt)
600     return CURLE_OUT_OF_MEMORY;
601
602   Curl_MD5_update(ctxt, (const unsigned char *) userp,
603                   curlx_uztoui(strlen(userp)));
604   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
605   Curl_MD5_update(ctxt, (const unsigned char *) realm,
606                   curlx_uztoui(strlen(realm)));
607   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
608   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
609                   curlx_uztoui(strlen(passwdp)));
610   Curl_MD5_final(ctxt, digest);
611
612   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
613   if(!ctxt)
614     return CURLE_OUT_OF_MEMORY;
615
616   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
617   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
618   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
619                   curlx_uztoui(strlen(nonce)));
620   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
621   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
622                   curlx_uztoui(strlen(cnonce)));
623   Curl_MD5_final(ctxt, digest);
624
625   /* Convert calculated 16 octet hex into 32 bytes string */
626   for(i = 0; i < MD5_DIGEST_LEN; i++)
627     snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
628
629   /* Generate our SPN */
630   spn = Curl_sasl_build_spn(service, realm);
631   if(!spn)
632     return CURLE_OUT_OF_MEMORY;
633
634   /* Calculate H(A2) */
635   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
636   if(!ctxt) {
637     Curl_safefree(spn);
638
639     return CURLE_OUT_OF_MEMORY;
640   }
641
642   Curl_MD5_update(ctxt, (const unsigned char *) method,
643                   curlx_uztoui(strlen(method)));
644   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
645   Curl_MD5_update(ctxt, (const unsigned char *) spn,
646                   curlx_uztoui(strlen(spn)));
647   Curl_MD5_final(ctxt, digest);
648
649   for(i = 0; i < MD5_DIGEST_LEN; i++)
650     snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
651
652   /* Now calculate the response hash */
653   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
654   if(!ctxt) {
655     Curl_safefree(spn);
656
657     return CURLE_OUT_OF_MEMORY;
658   }
659
660   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * 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
666   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
667                   curlx_uztoui(strlen(nonceCount)));
668   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
669   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
670                   curlx_uztoui(strlen(cnonce)));
671   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
672   Curl_MD5_update(ctxt, (const unsigned char *) qop,
673                   curlx_uztoui(strlen(qop)));
674   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
675
676   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
677   Curl_MD5_final(ctxt, digest);
678
679   for(i = 0; i < MD5_DIGEST_LEN; i++)
680     snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
681
682   /* Generate the response */
683   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
684                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
685                      "qop=%s",
686                      userp, realm, nonce,
687                      cnonce, nonceCount, spn, resp_hash_hex, qop);
688   Curl_safefree(spn);
689   if(!response)
690     return CURLE_OUT_OF_MEMORY;
691
692   /* Base64 encode the response */
693   result = Curl_base64_encode(data, response, 0, outptr, outlen);
694
695   Curl_safefree(response);
696
697   return result;
698 }
699
700 /*
701  * Curl_sasl_decode_digest_http_message()
702  *
703  * This is used to decode a HTTP DIGEST challenge message into the seperate
704  * attributes.
705  *
706  * Parameters:
707  *
708  * chlg    [in]     - The challenge message.
709  * digest  [in/out] - The digest data struct being used and modified.
710  *
711  * Returns CURLE_OK on success.
712  */
713 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
714                                               struct digestdata *digest)
715 {
716   bool before = FALSE; /* got a nonce before */
717   bool foundAuth = FALSE;
718   bool foundAuthInt = FALSE;
719   char *token = NULL;
720   char *tmp = NULL;
721
722   /* If we already have received a nonce, keep that in mind */
723   if(digest->nonce)
724     before = TRUE;
725
726   /* Clean up any former leftovers and initialise to defaults */
727   Curl_sasl_digest_cleanup(digest);
728
729   for(;;) {
730     char value[DIGEST_MAX_VALUE_LENGTH];
731     char content[DIGEST_MAX_CONTENT_LENGTH];
732
733     /* Pass all additional spaces here */
734     while(*chlg && ISSPACE(*chlg))
735       chlg++;
736
737     /* Extract a value=content pair */
738     if(!sasl_digest_get_pair(chlg, value, content, &chlg)) {
739       if(Curl_raw_equal(value, "nonce")) {
740         digest->nonce = strdup(content);
741         if(!digest->nonce)
742           return CURLE_OUT_OF_MEMORY;
743       }
744       else if(Curl_raw_equal(value, "stale")) {
745         if(Curl_raw_equal(content, "true")) {
746           digest->stale = TRUE;
747           digest->nc = 1; /* we make a new nonce now */
748         }
749       }
750       else if(Curl_raw_equal(value, "realm")) {
751         digest->realm = strdup(content);
752         if(!digest->realm)
753           return CURLE_OUT_OF_MEMORY;
754       }
755       else if(Curl_raw_equal(value, "opaque")) {
756         digest->opaque = strdup(content);
757         if(!digest->opaque)
758           return CURLE_OUT_OF_MEMORY;
759       }
760       else if(Curl_raw_equal(value, "qop")) {
761         char *tok_buf;
762         /* Tokenize the list and choose auth if possible, use a temporary
763             clone of the buffer since strtok_r() ruins it */
764         tmp = strdup(content);
765         if(!tmp)
766           return CURLE_OUT_OF_MEMORY;
767
768         token = strtok_r(tmp, ",", &tok_buf);
769         while(token != NULL) {
770           if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
771             foundAuth = TRUE;
772           }
773           else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
774             foundAuthInt = TRUE;
775           }
776           token = strtok_r(NULL, ",", &tok_buf);
777         }
778
779         free(tmp);
780
781         /* Select only auth or auth-int. Otherwise, ignore */
782         if(foundAuth) {
783           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
784           if(!digest->qop)
785             return CURLE_OUT_OF_MEMORY;
786         }
787         else if(foundAuthInt) {
788           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
789           if(!digest->qop)
790             return CURLE_OUT_OF_MEMORY;
791         }
792       }
793       else if(Curl_raw_equal(value, "algorithm")) {
794         digest->algorithm = strdup(content);
795         if(!digest->algorithm)
796           return CURLE_OUT_OF_MEMORY;
797
798         if(Curl_raw_equal(content, "MD5-sess"))
799           digest->algo = CURLDIGESTALGO_MD5SESS;
800         else if(Curl_raw_equal(content, "MD5"))
801           digest->algo = CURLDIGESTALGO_MD5;
802         else
803           return CURLE_BAD_CONTENT_ENCODING;
804       }
805       else {
806         /* unknown specifier, ignore it! */
807       }
808     }
809     else
810       break; /* we're done here */
811
812     /* Pass all additional spaces here */
813     while(*chlg && ISSPACE(*chlg))
814       chlg++;
815
816     /* Allow the list to be comma-separated */
817     if(',' == *chlg)
818       chlg++;
819   }
820
821   /* We had a nonce since before, and we got another one now without
822      'stale=true'. This means we provided bad credentials in the previous
823      request */
824   if(before && !digest->stale)
825     return CURLE_BAD_CONTENT_ENCODING;
826
827   /* We got this header without a nonce, that's a bad Digest line! */
828   if(!digest->nonce)
829     return CURLE_BAD_CONTENT_ENCODING;
830
831   return CURLE_OK;
832 }
833
834 /*
835  * Curl_sasl_create_digest_http_message()
836  *
837  * This is used to generate a HTTP DIGEST response message ready for sending
838  * to the recipient.
839  *
840  * Parameters:
841  *
842  * data    [in]     - The session handle.
843  * userp   [in]     - The user name.
844  * passdwp [in]     - The user's password.
845  * request [in]     - The HTTP request.
846  * uripath [in]     - The path of the HTTP uri.
847  * digest  [in/out] - The digest data struct being used and modified.
848  * outptr  [in/out] - The address where a pointer to newly allocated memory
849  *                    holding the result will be stored upon completion.
850  * outlen  [out]    - The length of the output message.
851  *
852  * Returns CURLE_OK on success.
853  */
854 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
855                                               const char *userp,
856                                               const char *passwdp,
857                                               const unsigned char *request,
858                                               const unsigned char *uripath,
859                                               struct digestdata *digest,
860                                               char **outptr, size_t *outlen)
861 {
862   CURLcode result;
863   unsigned char md5buf[16]; /* 16 bytes/128 bits */
864   unsigned char request_digest[33];
865   unsigned char *md5this;
866   unsigned char ha1[33];/* 32 digits and 1 zero byte */
867   unsigned char ha2[33];/* 32 digits and 1 zero byte */
868   char cnoncebuf[33];
869   char *cnonce = NULL;
870   size_t cnonce_sz = 0;
871   char *userp_quoted;
872   char *response = NULL;
873   char *tmp = NULL;
874
875   if(!digest->nc)
876     digest->nc = 1;
877
878   if(!digest->cnonce) {
879     snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x",
880              Curl_rand(data), Curl_rand(data),
881              Curl_rand(data), Curl_rand(data));
882
883     result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
884                                 &cnonce, &cnonce_sz);
885     if(result)
886       return result;
887
888     digest->cnonce = cnonce;
889   }
890
891   /*
892     if the algorithm is "MD5" or unspecified (which then defaults to MD5):
893
894     A1 = unq(username-value) ":" unq(realm-value) ":" passwd
895
896     if the algorithm is "MD5-sess" then:
897
898     A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
899          ":" unq(nonce-value) ":" unq(cnonce-value)
900   */
901
902   md5this = (unsigned char *)
903     aprintf("%s:%s:%s", userp, digest->realm, passwdp);
904   if(!md5this)
905     return CURLE_OUT_OF_MEMORY;
906
907   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
908   Curl_md5it(md5buf, md5this);
909   Curl_safefree(md5this);
910   sasl_digest_md5_to_ascii(md5buf, ha1);
911
912   if(digest->algo == CURLDIGESTALGO_MD5SESS) {
913     /* nonce and cnonce are OUTSIDE the hash */
914     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
915     if(!tmp)
916       return CURLE_OUT_OF_MEMORY;
917
918     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
919     Curl_md5it(md5buf, (unsigned char *)tmp);
920     Curl_safefree(tmp);
921     sasl_digest_md5_to_ascii(md5buf, ha1);
922   }
923
924   /*
925     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
926
927       A2       = Method ":" digest-uri-value
928
929           If the "qop" value is "auth-int", then A2 is:
930
931       A2       = Method ":" digest-uri-value ":" H(entity-body)
932
933     (The "Method" value is the HTTP request method as specified in section
934     5.1.1 of RFC 2616)
935   */
936
937   md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
938
939   if(digest->qop && Curl_raw_equal(digest->qop, "auth-int")) {
940     /* We don't support auth-int for PUT or POST at the moment.
941        TODO: replace md5 of empty string with entity-body for PUT/POST */
942     unsigned char *md5this2 = (unsigned char *)
943       aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
944     Curl_safefree(md5this);
945     md5this = md5this2;
946   }
947
948   if(!md5this)
949     return CURLE_OUT_OF_MEMORY;
950
951   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
952   Curl_md5it(md5buf, md5this);
953   Curl_safefree(md5this);
954   sasl_digest_md5_to_ascii(md5buf, ha2);
955
956   if(digest->qop) {
957     md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
958                                        ha1,
959                                        digest->nonce,
960                                        digest->nc,
961                                        digest->cnonce,
962                                        digest->qop,
963                                        ha2);
964   }
965   else {
966     md5this = (unsigned char *)aprintf("%s:%s:%s",
967                                        ha1,
968                                        digest->nonce,
969                                        ha2);
970   }
971
972   if(!md5this)
973     return CURLE_OUT_OF_MEMORY;
974
975   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
976   Curl_md5it(md5buf, md5this);
977   Curl_safefree(md5this);
978   sasl_digest_md5_to_ascii(md5buf, request_digest);
979
980   /* for test case 64 (snooped from a Mozilla 1.3a request)
981
982     Authorization: Digest username="testuser", realm="testrealm", \
983     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
984
985     Digest parameters are all quoted strings.  Username which is provided by
986     the user will need double quotes and backslashes within it escaped.  For
987     the other fields, this shouldn't be an issue.  realm, nonce, and opaque
988     are copied as is from the server, escapes and all.  cnonce is generated
989     with web-safe characters.  uri is already percent encoded.  nc is 8 hex
990     characters.  algorithm and qop with standard values only contain web-safe
991     chracters.
992   */
993   userp_quoted = sasl_digest_string_quoted(userp);
994   if(!userp_quoted)
995     return CURLE_OUT_OF_MEMORY;
996
997   if(digest->qop) {
998     response = aprintf("username=\"%s\", "
999                        "realm=\"%s\", "
1000                        "nonce=\"%s\", "
1001                        "uri=\"%s\", "
1002                        "cnonce=\"%s\", "
1003                        "nc=%08x, "
1004                        "qop=%s, "
1005                        "response=\"%s\"",
1006                        userp_quoted,
1007                        digest->realm,
1008                        digest->nonce,
1009                        uripath,
1010                        digest->cnonce,
1011                        digest->nc,
1012                        digest->qop,
1013                        request_digest);
1014
1015     if(Curl_raw_equal(digest->qop, "auth"))
1016       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
1017                        padded which tells to the server how many times you are
1018                        using the same nonce in the qop=auth mode */
1019   }
1020   else {
1021     response = aprintf("username=\"%s\", "
1022                        "realm=\"%s\", "
1023                        "nonce=\"%s\", "
1024                        "uri=\"%s\", "
1025                        "response=\"%s\"",
1026                        userp_quoted,
1027                        digest->realm,
1028                        digest->nonce,
1029                        uripath,
1030                        request_digest);
1031   }
1032   Curl_safefree(userp_quoted);
1033   if(!response)
1034     return CURLE_OUT_OF_MEMORY;
1035
1036   /* Add the optional fields */
1037   if(digest->opaque) {
1038     /* Append the opaque */
1039     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
1040     free(response);
1041     if(!tmp)
1042       return CURLE_OUT_OF_MEMORY;
1043
1044     response = tmp;
1045   }
1046
1047   if(digest->algorithm) {
1048     /* Append the algorithm */
1049     tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
1050     free(response);
1051     if(!tmp)
1052       return CURLE_OUT_OF_MEMORY;
1053
1054     response = tmp;
1055   }
1056
1057   /* Return the output */
1058   *outptr = response;
1059   *outlen = strlen(response);
1060
1061   return CURLE_OK;
1062 }
1063
1064 /*
1065  * Curl_sasl_digest_cleanup()
1066  *
1067  * This is used to clean up the digest specific data.
1068  *
1069  * Parameters:
1070  *
1071  * digest    [in/out] - The digest data struct being cleaned up.
1072  *
1073  */
1074 void Curl_sasl_digest_cleanup(struct digestdata *digest)
1075 {
1076   Curl_safefree(digest->nonce);
1077   Curl_safefree(digest->cnonce);
1078   Curl_safefree(digest->realm);
1079   Curl_safefree(digest->opaque);
1080   Curl_safefree(digest->qop);
1081   Curl_safefree(digest->algorithm);
1082
1083   digest->nc = 0;
1084   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
1085   digest->stale = FALSE; /* default means normal, not stale */
1086 }
1087 #endif  /* !USE_WINDOWS_SSPI */
1088
1089 #endif  /* CURL_DISABLE_CRYPTO_AUTH */
1090
1091 #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
1092 /*
1093  * Curl_sasl_ntlm_cleanup()
1094  *
1095  * This is used to clean up the ntlm specific data.
1096  *
1097  * Parameters:
1098  *
1099  * ntlm    [in/out] - The ntlm data struct being cleaned up.
1100  *
1101  */
1102 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
1103 {
1104   /* Free the target info */
1105   Curl_safefree(ntlm->target_info);
1106
1107   /* Reset any variables */
1108   ntlm->target_info_len = 0;
1109 }
1110 #endif /* USE_NTLM && !USE_WINDOWS_SSPI*/
1111
1112 /*
1113  * Curl_sasl_create_xoauth2_message()
1114  *
1115  * This is used to generate an already encoded OAuth 2.0 message ready for
1116  * sending to the recipient.
1117  *
1118  * Parameters:
1119  *
1120  * data    [in]     - The session handle.
1121  * user    [in]     - The user name.
1122  * bearer  [in]     - The bearer token.
1123  * outptr  [in/out] - The address where a pointer to newly allocated memory
1124  *                    holding the result will be stored upon completion.
1125  * outlen  [out]    - The length of the output message.
1126  *
1127  * Returns CURLE_OK on success.
1128  */
1129 CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data,
1130                                           const char *user,
1131                                           const char *bearer,
1132                                           char **outptr, size_t *outlen)
1133 {
1134   CURLcode result = CURLE_OK;
1135   char *xoauth = NULL;
1136
1137   /* Generate the message */
1138   xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
1139   if(!xoauth)
1140     return CURLE_OUT_OF_MEMORY;
1141
1142   /* Base64 encode the reply */
1143   result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen);
1144
1145   Curl_safefree(xoauth);
1146
1147   return result;
1148 }
1149
1150 /*
1151  * Curl_sasl_cleanup()
1152  *
1153  * This is used to cleanup any libraries or curl modules used by the sasl
1154  * functions.
1155  *
1156  * Parameters:
1157  *
1158  * conn     [in]     - The connection data.
1159  * authused [in]     - The authentication mechanism used.
1160  */
1161 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
1162 {
1163 #if defined(USE_KERBEROS5)
1164   /* Cleanup the gssapi structure */
1165   if(authused == SASL_MECH_GSSAPI) {
1166     Curl_sasl_gssapi_cleanup(&conn->krb5);
1167   }
1168 #endif
1169
1170 #if defined(USE_NTLM)
1171   /* Cleanup the ntlm structure */
1172   if(authused == SASL_MECH_NTLM) {
1173     Curl_sasl_ntlm_cleanup(&conn->ntlm);
1174   }
1175 #endif
1176
1177 #if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
1178   /* Reserved for future use */
1179   (void)conn;
1180   (void)authused;
1181 #endif
1182 }