source: UsbWattMeter/trunk/curl-7.47.1/lib/curl_sasl.c@ 164

Last change on this file since 164 was 164, checked in by coas-nagasima, 6 years ago

TOPPERS/ECNLサンプルアプリ「USB充電器電力計」を追加

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