digest.c
Go to the documentation of this file.
1 /***************************************************************************
2  * _ _ ____ _
3  * Project ___| | | | _ \| |
4  * / __| | | | |_) | |
5  * | (__| |_| | _ <| |___
6  * \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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  * RFC2831 DIGEST-MD5 authentication
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
28 
29 #include <curl/curl.h>
30 
31 #include "vauth/vauth.h"
32 #include "vauth/digest.h"
33 #include "urldata.h"
34 #include "curl_base64.h"
35 #include "curl_hmac.h"
36 #include "curl_md5.h"
37 #include "vtls/vtls.h"
38 #include "warnless.h"
39 #include "strtok.h"
40 #include "strcase.h"
41 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
42 #include "curl_printf.h"
43 #include "rand.h"
44 
45 /* The last #include files should be: */
46 #include "curl_memory.h"
47 #include "memdebug.h"
48 
49 #if !defined(USE_WINDOWS_SSPI)
50 #define DIGEST_QOP_VALUE_AUTH (1 << 0)
51 #define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
52 #define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
53 
54 #define DIGEST_QOP_VALUE_STRING_AUTH "auth"
55 #define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int"
56 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
57 
58 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
59  It converts digest text to ASCII so the MD5 will be correct for
60  what ultimately goes over the network.
61 */
62 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
63  result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \
64  if(result) { \
65  free(b); \
66  return result; \
67  }
68 #endif /* !USE_WINDOWS_SSPI */
69 
70 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
71  const char **endptr)
72 {
73  int c;
74  bool starts_with_quote = FALSE;
75  bool escape = FALSE;
76 
77  for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
78  *value++ = *str++;
79  *value = 0;
80 
81  if('=' != *str++)
82  /* eek, no match */
83  return FALSE;
84 
85  if('\"' == *str) {
86  /* This starts with a quote so it must end with one as well! */
87  str++;
88  starts_with_quote = TRUE;
89  }
90 
91  for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
92  switch(*str) {
93  case '\\':
94  if(!escape) {
95  /* possibly the start of an escaped quote */
96  escape = TRUE;
97  *content++ = '\\'; /* Even though this is an escape character, we still
98  store it as-is in the target buffer */
99  continue;
100  }
101  break;
102 
103  case ',':
104  if(!starts_with_quote) {
105  /* This signals the end of the content if we didn't get a starting
106  quote and then we do "sloppy" parsing */
107  c = 0; /* the end */
108  continue;
109  }
110  break;
111 
112  case '\r':
113  case '\n':
114  /* end of string */
115  c = 0;
116  continue;
117 
118  case '\"':
119  if(!escape && starts_with_quote) {
120  /* end of string */
121  c = 0;
122  continue;
123  }
124  break;
125  }
126 
127  escape = FALSE;
128  *content++ = *str;
129  }
130 
131  *content = 0;
132  *endptr = str;
133 
134  return TRUE;
135 }
136 
137 #if !defined(USE_WINDOWS_SSPI)
138 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
139 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
140  unsigned char *dest) /* 33 bytes */
141 {
142  int i;
143  for(i = 0; i < 16; i++)
144  snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
145 }
146 
147 /* Perform quoted-string escaping as described in RFC2616 and its errata */
148 static char *auth_digest_string_quoted(const char *source)
149 {
150  char *dest, *d;
151  const char *s = source;
152  size_t n = 1; /* null terminator */
153 
154  /* Calculate size needed */
155  while(*s) {
156  ++n;
157  if(*s == '"' || *s == '\\') {
158  ++n;
159  }
160  ++s;
161  }
162 
163  dest = malloc(n);
164  if(dest) {
165  s = source;
166  d = dest;
167  while(*s) {
168  if(*s == '"' || *s == '\\') {
169  *d++ = '\\';
170  }
171  *d++ = *s++;
172  }
173  *d = 0;
174  }
175 
176  return dest;
177 }
178 
179 /* Retrieves the value for a corresponding key from the challenge string
180  * returns TRUE if the key could be found, FALSE if it does not exists
181  */
182 static bool auth_digest_get_key_value(const char *chlg,
183  const char *key,
184  char *value,
185  size_t max_val_len,
186  char end_char)
187 {
188  char *find_pos;
189  size_t i;
190 
191  find_pos = strstr(chlg, key);
192  if(!find_pos)
193  return FALSE;
194 
195  find_pos += strlen(key);
196 
197  for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
198  value[i] = *find_pos++;
199  value[i] = '\0';
200 
201  return TRUE;
202 }
203 
204 static CURLcode auth_digest_get_qop_values(const char *options, int *value)
205 {
206  char *tmp;
207  char *token;
208  char *tok_buf = NULL;
209 
210  /* Initialise the output */
211  *value = 0;
212 
213  /* Tokenise the list of qop values. Use a temporary clone of the buffer since
214  strtok_r() ruins it. */
215  tmp = strdup(options);
216  if(!tmp)
217  return CURLE_OUT_OF_MEMORY;
218 
219  token = strtok_r(tmp, ",", &tok_buf);
220  while(token != NULL) {
222  *value |= DIGEST_QOP_VALUE_AUTH;
224  *value |= DIGEST_QOP_VALUE_AUTH_INT;
226  *value |= DIGEST_QOP_VALUE_AUTH_CONF;
227 
228  token = strtok_r(NULL, ",", &tok_buf);
229  }
230 
231  free(tmp);
232 
233  return CURLE_OK;
234 }
235 
236 /*
237  * auth_decode_digest_md5_message()
238  *
239  * This is used internally to decode an already encoded DIGEST-MD5 challenge
240  * message into the separate attributes.
241  *
242  * Parameters:
243  *
244  * chlg64 [in] - The base64 encoded challenge message.
245  * nonce [in/out] - The buffer where the nonce will be stored.
246  * nlen [in] - The length of the nonce buffer.
247  * realm [in/out] - The buffer where the realm will be stored.
248  * rlen [in] - The length of the realm buffer.
249  * alg [in/out] - The buffer where the algorithm will be stored.
250  * alen [in] - The length of the algorithm buffer.
251  * qop [in/out] - The buffer where the qop-options will be stored.
252  * qlen [in] - The length of the qop buffer.
253  *
254  * Returns CURLE_OK on success.
255  */
256 static CURLcode auth_decode_digest_md5_message(const char *chlg64,
257  char *nonce, size_t nlen,
258  char *realm, size_t rlen,
259  char *alg, size_t alen,
260  char *qop, size_t qlen)
261 {
263  unsigned char *chlg = NULL;
264  size_t chlglen = 0;
265  size_t chlg64len = strlen(chlg64);
266 
267  /* Decode the base-64 encoded challenge message */
268  if(chlg64len && *chlg64 != '=') {
269  result = Curl_base64_decode(chlg64, &chlg, &chlglen);
270  if(result)
271  return result;
272  }
273 
274  /* Ensure we have a valid challenge message */
275  if(!chlg)
277 
278  /* Retrieve nonce string from the challenge */
279  if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen,
280  '\"')) {
281  free(chlg);
283  }
284 
285  /* Retrieve realm string from the challenge */
286  if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
287  '\"')) {
288  /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
289  strcpy(realm, "");
290  }
291 
292  /* Retrieve algorithm string from the challenge */
293  if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
294  free(chlg);
296  }
297 
298  /* Retrieve qop-options string from the challenge */
299  if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) {
300  free(chlg);
302  }
303 
304  free(chlg);
305 
306  return CURLE_OK;
307 }
308 
309 /*
310  * Curl_auth_is_digest_supported()
311  *
312  * This is used to evaluate if DIGEST is supported.
313  *
314  * Parameters: None
315  *
316  * Returns TRUE as DIGEST as handled by libcurl.
317  */
319 {
320  return TRUE;
321 }
322 
323 /*
324  * Curl_auth_create_digest_md5_message()
325  *
326  * This is used to generate an already encoded DIGEST-MD5 response message
327  * ready for sending to the recipient.
328  *
329  * Parameters:
330  *
331  * data [in] - The session handle.
332  * chlg64 [in] - The base64 encoded challenge message.
333  * userp [in] - The user name.
334  * passdwp [in] - The user's password.
335  * service [in] - The service type such as http, smtp, pop or imap.
336  * outptr [in/out] - The address where a pointer to newly allocated memory
337  * holding the result will be stored upon completion.
338  * outlen [out] - The length of the output message.
339  *
340  * Returns CURLE_OK on success.
341  */
343  const char *chlg64,
344  const char *userp,
345  const char *passwdp,
346  const char *service,
347  char **outptr, size_t *outlen)
348 {
350  size_t i;
351  MD5_context *ctxt;
352  char *response = NULL;
353  unsigned char digest[MD5_DIGEST_LEN];
354  char HA1_hex[2 * MD5_DIGEST_LEN + 1];
355  char HA2_hex[2 * MD5_DIGEST_LEN + 1];
356  char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
357  char nonce[64];
358  char realm[128];
359  char algorithm[64];
360  char qop_options[64];
361  int qop_values;
362  char cnonce[33];
363  char nonceCount[] = "00000001";
364  char method[] = "AUTHENTICATE";
365  char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
366  char *spn = NULL;
367 
368  /* Decode the challenge message */
369  result = auth_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
370  realm, sizeof(realm),
371  algorithm, sizeof(algorithm),
372  qop_options, sizeof(qop_options));
373  if(result)
374  return result;
375 
376  /* We only support md5 sessions */
377  if(strcmp(algorithm, "md5-sess") != 0)
379 
380  /* Get the qop-values from the qop-options */
381  result = auth_digest_get_qop_values(qop_options, &qop_values);
382  if(result)
383  return result;
384 
385  /* We only support auth quality-of-protection */
386  if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
388 
389  /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
390  result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
391  if(result)
392  return result;
393 
394  /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
396  if(!ctxt)
397  return CURLE_OUT_OF_MEMORY;
398 
399  Curl_MD5_update(ctxt, (const unsigned char *) userp,
400  curlx_uztoui(strlen(userp)));
401  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
402  Curl_MD5_update(ctxt, (const unsigned char *) realm,
403  curlx_uztoui(strlen(realm)));
404  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
405  Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
406  curlx_uztoui(strlen(passwdp)));
407  Curl_MD5_final(ctxt, digest);
408 
410  if(!ctxt)
411  return CURLE_OUT_OF_MEMORY;
412 
413  Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
414  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
415  Curl_MD5_update(ctxt, (const unsigned char *) nonce,
416  curlx_uztoui(strlen(nonce)));
417  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
418  Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
419  curlx_uztoui(strlen(cnonce)));
420  Curl_MD5_final(ctxt, digest);
421 
422  /* Convert calculated 16 octet hex into 32 bytes string */
423  for(i = 0; i < MD5_DIGEST_LEN; i++)
424  snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
425 
426  /* Generate our SPN */
427  spn = Curl_auth_build_spn(service, realm, NULL);
428  if(!spn)
429  return CURLE_OUT_OF_MEMORY;
430 
431  /* Calculate H(A2) */
433  if(!ctxt) {
434  free(spn);
435 
436  return CURLE_OUT_OF_MEMORY;
437  }
438 
439  Curl_MD5_update(ctxt, (const unsigned char *) method,
440  curlx_uztoui(strlen(method)));
441  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
442  Curl_MD5_update(ctxt, (const unsigned char *) spn,
443  curlx_uztoui(strlen(spn)));
444  Curl_MD5_final(ctxt, digest);
445 
446  for(i = 0; i < MD5_DIGEST_LEN; i++)
447  snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
448 
449  /* Now calculate the response hash */
451  if(!ctxt) {
452  free(spn);
453 
454  return CURLE_OUT_OF_MEMORY;
455  }
456 
457  Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
458  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
459  Curl_MD5_update(ctxt, (const unsigned char *) nonce,
460  curlx_uztoui(strlen(nonce)));
461  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
462 
463  Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
464  curlx_uztoui(strlen(nonceCount)));
465  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
466  Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
467  curlx_uztoui(strlen(cnonce)));
468  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
469  Curl_MD5_update(ctxt, (const unsigned char *) qop,
470  curlx_uztoui(strlen(qop)));
471  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
472 
473  Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
474  Curl_MD5_final(ctxt, digest);
475 
476  for(i = 0; i < MD5_DIGEST_LEN; i++)
477  snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
478 
479  /* Generate the response */
480  response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
481  "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
482  "qop=%s",
483  userp, realm, nonce,
484  cnonce, nonceCount, spn, resp_hash_hex, qop);
485  free(spn);
486  if(!response)
487  return CURLE_OUT_OF_MEMORY;
488 
489  /* Base64 encode the response */
490  result = Curl_base64_encode(data, response, 0, outptr, outlen);
491 
492  free(response);
493 
494  return result;
495 }
496 
497 /*
498  * Curl_auth_decode_digest_http_message()
499  *
500  * This is used to decode a HTTP DIGEST challenge message into the separate
501  * attributes.
502  *
503  * Parameters:
504  *
505  * chlg [in] - The challenge message.
506  * digest [in/out] - The digest data struct being used and modified.
507  *
508  * Returns CURLE_OK on success.
509  */
511  struct digestdata *digest)
512 {
513  bool before = FALSE; /* got a nonce before */
514  bool foundAuth = FALSE;
515  bool foundAuthInt = FALSE;
516  char *token = NULL;
517  char *tmp = NULL;
518 
519  /* If we already have received a nonce, keep that in mind */
520  if(digest->nonce)
521  before = TRUE;
522 
523  /* Clean up any former leftovers and initialise to defaults */
524  Curl_auth_digest_cleanup(digest);
525 
526  for(;;) {
528  char content[DIGEST_MAX_CONTENT_LENGTH];
529 
530  /* Pass all additional spaces here */
531  while(*chlg && ISSPACE(*chlg))
532  chlg++;
533 
534  /* Extract a value=content pair */
535  if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
536  if(strcasecompare(value, "nonce")) {
537  free(digest->nonce);
538  digest->nonce = strdup(content);
539  if(!digest->nonce)
540  return CURLE_OUT_OF_MEMORY;
541  }
542  else if(strcasecompare(value, "stale")) {
543  if(strcasecompare(content, "true")) {
544  digest->stale = TRUE;
545  digest->nc = 1; /* we make a new nonce now */
546  }
547  }
548  else if(strcasecompare(value, "realm")) {
549  free(digest->realm);
550  digest->realm = strdup(content);
551  if(!digest->realm)
552  return CURLE_OUT_OF_MEMORY;
553  }
554  else if(strcasecompare(value, "opaque")) {
555  free(digest->opaque);
556  digest->opaque = strdup(content);
557  if(!digest->opaque)
558  return CURLE_OUT_OF_MEMORY;
559  }
560  else if(strcasecompare(value, "qop")) {
561  char *tok_buf = NULL;
562  /* Tokenize the list and choose auth if possible, use a temporary
563  clone of the buffer since strtok_r() ruins it */
564  tmp = strdup(content);
565  if(!tmp)
566  return CURLE_OUT_OF_MEMORY;
567 
568  token = strtok_r(tmp, ",", &tok_buf);
569  while(token != NULL) {
571  foundAuth = TRUE;
572  }
574  foundAuthInt = TRUE;
575  }
576  token = strtok_r(NULL, ",", &tok_buf);
577  }
578 
579  free(tmp);
580 
581  /* Select only auth or auth-int. Otherwise, ignore */
582  if(foundAuth) {
583  free(digest->qop);
585  if(!digest->qop)
586  return CURLE_OUT_OF_MEMORY;
587  }
588  else if(foundAuthInt) {
589  free(digest->qop);
591  if(!digest->qop)
592  return CURLE_OUT_OF_MEMORY;
593  }
594  }
595  else if(strcasecompare(value, "algorithm")) {
596  free(digest->algorithm);
597  digest->algorithm = strdup(content);
598  if(!digest->algorithm)
599  return CURLE_OUT_OF_MEMORY;
600 
601  if(strcasecompare(content, "MD5-sess"))
602  digest->algo = CURLDIGESTALGO_MD5SESS;
603  else if(strcasecompare(content, "MD5"))
604  digest->algo = CURLDIGESTALGO_MD5;
605  else
607  }
608  else {
609  /* Unknown specifier, ignore it! */
610  }
611  }
612  else
613  break; /* We're done here */
614 
615  /* Pass all additional spaces here */
616  while(*chlg && ISSPACE(*chlg))
617  chlg++;
618 
619  /* Allow the list to be comma-separated */
620  if(',' == *chlg)
621  chlg++;
622  }
623 
624  /* We had a nonce since before, and we got another one now without
625  'stale=true'. This means we provided bad credentials in the previous
626  request */
627  if(before && !digest->stale)
629 
630  /* We got this header without a nonce, that's a bad Digest line! */
631  if(!digest->nonce)
633 
634  return CURLE_OK;
635 }
636 
637 /*
638  * Curl_auth_create_digest_http_message()
639  *
640  * This is used to generate a HTTP DIGEST response message ready for sending
641  * to the recipient.
642  *
643  * Parameters:
644  *
645  * data [in] - The session handle.
646  * userp [in] - The user name.
647  * passdwp [in] - The user's password.
648  * request [in] - The HTTP request.
649  * uripath [in] - The path of the HTTP uri.
650  * digest [in/out] - The digest data struct being used and modified.
651  * outptr [in/out] - The address where a pointer to newly allocated memory
652  * holding the result will be stored upon completion.
653  * outlen [out] - The length of the output message.
654  *
655  * Returns CURLE_OK on success.
656  */
658  const char *userp,
659  const char *passwdp,
660  const unsigned char *request,
661  const unsigned char *uripath,
662  struct digestdata *digest,
663  char **outptr, size_t *outlen)
664 {
666  unsigned char md5buf[16]; /* 16 bytes/128 bits */
667  unsigned char request_digest[33];
668  unsigned char *md5this;
669  unsigned char ha1[33]; /* 32 digits and 1 zero byte */
670  unsigned char ha2[33]; /* 32 digits and 1 zero byte */
671  char cnoncebuf[33];
672  char *cnonce = NULL;
673  size_t cnonce_sz = 0;
674  char *userp_quoted;
675  char *response = NULL;
676  char *tmp = NULL;
677 
678  if(!digest->nc)
679  digest->nc = 1;
680 
681  if(!digest->cnonce) {
682  result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
683  sizeof(cnoncebuf));
684  if(result)
685  return result;
686 
687  result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
688  &cnonce, &cnonce_sz);
689  if(result)
690  return result;
691 
692  digest->cnonce = cnonce;
693  }
694 
695  /*
696  If the algorithm is "MD5" or unspecified (which then defaults to MD5):
697 
698  A1 = unq(username-value) ":" unq(realm-value) ":" passwd
699 
700  If the algorithm is "MD5-sess" then:
701 
702  A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
703  unq(nonce-value) ":" unq(cnonce-value)
704  */
705 
706  md5this = (unsigned char *)
707  aprintf("%s:%s:%s", userp, digest->realm, passwdp);
708  if(!md5this)
709  return CURLE_OUT_OF_MEMORY;
710 
711  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
712  Curl_md5it(md5buf, md5this);
713  free(md5this);
714  auth_digest_md5_to_ascii(md5buf, ha1);
715 
716  if(digest->algo == CURLDIGESTALGO_MD5SESS) {
717  /* nonce and cnonce are OUTSIDE the hash */
718  tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
719  if(!tmp)
720  return CURLE_OUT_OF_MEMORY;
721 
722  CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
723  Curl_md5it(md5buf, (unsigned char *) tmp);
724  free(tmp);
725  auth_digest_md5_to_ascii(md5buf, ha1);
726  }
727 
728  /*
729  If the "qop" directive's value is "auth" or is unspecified, then A2 is:
730 
731  A2 = Method ":" digest-uri-value
732 
733  If the "qop" value is "auth-int", then A2 is:
734 
735  A2 = Method ":" digest-uri-value ":" H(entity-body)
736 
737  (The "Method" value is the HTTP request method as specified in section
738  5.1.1 of RFC 2616)
739  */
740 
741  md5this = (unsigned char *) aprintf("%s:%s", request, uripath);
742 
743  if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
744  /* We don't support auth-int for PUT or POST at the moment.
745  TODO: replace md5 of empty string with entity-body for PUT/POST */
746  unsigned char *md5this2 = (unsigned char *)
747  aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
748  free(md5this);
749  md5this = md5this2;
750  }
751 
752  if(!md5this)
753  return CURLE_OUT_OF_MEMORY;
754 
755  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
756  Curl_md5it(md5buf, md5this);
757  free(md5this);
758  auth_digest_md5_to_ascii(md5buf, ha2);
759 
760  if(digest->qop) {
761  md5this = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
762  ha1,
763  digest->nonce,
764  digest->nc,
765  digest->cnonce,
766  digest->qop,
767  ha2);
768  }
769  else {
770  md5this = (unsigned char *) aprintf("%s:%s:%s",
771  ha1,
772  digest->nonce,
773  ha2);
774  }
775 
776  if(!md5this)
777  return CURLE_OUT_OF_MEMORY;
778 
779  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
780  Curl_md5it(md5buf, md5this);
781  free(md5this);
782  auth_digest_md5_to_ascii(md5buf, request_digest);
783 
784  /* For test case 64 (snooped from a Mozilla 1.3a request)
785 
786  Authorization: Digest username="testuser", realm="testrealm", \
787  nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
788 
789  Digest parameters are all quoted strings. Username which is provided by
790  the user will need double quotes and backslashes within it escaped. For
791  the other fields, this shouldn't be an issue. realm, nonce, and opaque
792  are copied as is from the server, escapes and all. cnonce is generated
793  with web-safe characters. uri is already percent encoded. nc is 8 hex
794  characters. algorithm and qop with standard values only contain web-safe
795  characters.
796  */
797  userp_quoted = auth_digest_string_quoted(userp);
798  if(!userp_quoted)
799  return CURLE_OUT_OF_MEMORY;
800 
801  if(digest->qop) {
802  response = aprintf("username=\"%s\", "
803  "realm=\"%s\", "
804  "nonce=\"%s\", "
805  "uri=\"%s\", "
806  "cnonce=\"%s\", "
807  "nc=%08x, "
808  "qop=%s, "
809  "response=\"%s\"",
810  userp_quoted,
811  digest->realm,
812  digest->nonce,
813  uripath,
814  digest->cnonce,
815  digest->nc,
816  digest->qop,
817  request_digest);
818 
819  if(strcasecompare(digest->qop, "auth"))
820  digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
821  padded which tells to the server how many times you are
822  using the same nonce in the qop=auth mode */
823  }
824  else {
825  response = aprintf("username=\"%s\", "
826  "realm=\"%s\", "
827  "nonce=\"%s\", "
828  "uri=\"%s\", "
829  "response=\"%s\"",
830  userp_quoted,
831  digest->realm,
832  digest->nonce,
833  uripath,
834  request_digest);
835  }
836  free(userp_quoted);
837  if(!response)
838  return CURLE_OUT_OF_MEMORY;
839 
840  /* Add the optional fields */
841  if(digest->opaque) {
842  /* Append the opaque */
843  tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
844  free(response);
845  if(!tmp)
846  return CURLE_OUT_OF_MEMORY;
847 
848  response = tmp;
849  }
850 
851  if(digest->algorithm) {
852  /* Append the algorithm */
853  tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
854  free(response);
855  if(!tmp)
856  return CURLE_OUT_OF_MEMORY;
857 
858  response = tmp;
859  }
860 
861  /* Return the output */
862  *outptr = response;
863  *outlen = strlen(response);
864 
865  return CURLE_OK;
866 }
867 
868 /*
869  * Curl_auth_digest_cleanup()
870  *
871  * This is used to clean up the digest specific data.
872  *
873  * Parameters:
874  *
875  * digest [in/out] - The digest data struct being cleaned up.
876  *
877  */
879 {
880  Curl_safefree(digest->nonce);
881  Curl_safefree(digest->cnonce);
882  Curl_safefree(digest->realm);
883  Curl_safefree(digest->opaque);
884  Curl_safefree(digest->qop);
885  Curl_safefree(digest->algorithm);
886 
887  digest->nc = 0;
888  digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
889  digest->stale = FALSE; /* default means normal, not stale */
890 }
891 #endif /* !USE_WINDOWS_SSPI */
892 
893 #endif /* CURL_DISABLE_CRYPTO_AUTH */
#define free(ptr)
Definition: curl_memory.h:130
d
#define DIGEST_QOP_VALUE_AUTH_INT
Definition: digest.c:51
#define CURL_OUTPUT_DIGEST_CONV(a, b)
Definition: digest.c:62
#define DIGEST_QOP_VALUE_AUTH_CONF
Definition: digest.c:52
#define DIGEST_QOP_VALUE_AUTH
Definition: digest.c:50
const MD5_params Curl_DIGEST_MD5[1]
Definition: md5.c:498
CURLcode Curl_base64_decode(const char *src, unsigned char **outptr, size_t *outlen)
Definition: base64.c:100
int Curl_MD5_update(MD5_context *context, const unsigned char *data, unsigned int len)
Definition: md5.c:544
static CURLcode auth_digest_get_qop_values(const char *options, int *value)
Definition: digest.c:204
MD5_context * Curl_MD5_init(const MD5_params *md5params)
Definition: md5.c:520
int algo
Definition: urldata.h:283
#define strdup(ptr)
Definition: curl_memory.h:122
XmlRpcServer s
CURLcode Curl_base64_encode(struct Curl_easy *data, const char *inputbuff, size_t insize, char **outptr, size_t *outlen)
Definition: base64.c:291
CURLcode
Definition: curl.h:454
#define DIGEST_QOP_VALUE_STRING_AUTH
Definition: digest.c:54
#define DIGEST_QOP_VALUE_STRING_AUTH_INT
Definition: digest.c:55
#define strcasecompare(a, b)
Definition: strcase.h:35
#define malloc(size)
Definition: curl_memory.h:124
CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, const char *userp, const char *passwdp, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen)
Definition: digest.c:657
UNITTEST_START int result
Definition: unit1304.c:49
unsigned int i
Definition: unit1303.c:79
char * nonce
Definition: urldata.h:280
#define DIGEST_MAX_CONTENT_LENGTH
char * opaque
Definition: urldata.h:285
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const char *chlg64, const char *userp, const char *passwdp, const char *service, char **outptr, size_t *outlen)
Definition: digest.c:342
int nc
Definition: urldata.h:288
const char * str
Definition: unit1398.c:33
#define FALSE
bool stale
Definition: urldata.h:284
char * algorithm
Definition: urldata.h:287
CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num)
Definition: rand.c:151
void Curl_md5it(unsigned char *output, const unsigned char *input)
Definition: md5.c:511
CURLcode Curl_auth_decode_digest_http_message(const char *chlg, struct digestdata *digest)
Definition: digest.c:510
static bool auth_digest_get_key_value(const char *chlg, const char *key, char *value, size_t max_val_len, char end_char)
Definition: digest.c:182
bool Curl_auth_is_digest_supported(void)
Definition: digest.c:318
int Curl_MD5_final(MD5_context *context, unsigned char *result)
Definition: md5.c:553
void Curl_auth_digest_cleanup(struct digestdata *digest)
Definition: digest.c:878
unsigned int curlx_uztoui(size_t uznum)
Definition: warnless.c:243
Definition: curl.h:455
#define DIGEST_QOP_VALUE_STRING_AUTH_CONF
Definition: digest.c:56
#define Curl_safefree(ptr)
Definition: memdebug.h:170
#define ISSPACE(x)
#define aprintf
Definition: curl_printf.h:46
char * Curl_auth_build_spn(const char *service, const char *host, const char *realm)
Definition: vauth.c:53
#define strtok_r
Definition: strtok.h:29
char * cnonce
Definition: urldata.h:281
#define MD5_DIGEST_LEN
Definition: curl_md5.h:28
UNITTEST_START int * value
Definition: unit1602.c:51
char * qop
Definition: urldata.h:286
#define DIGEST_MAX_VALUE_LENGTH
#define snprintf
Definition: curl_printf.h:42
#define TRUE
bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, const char **endptr)
Definition: digest.c:70
char * realm
Definition: urldata.h:282
static CURLcode auth_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)
Definition: digest.c:256
int key
Definition: unit1602.c:56
Definition: debug.c:29
static char * auth_digest_string_quoted(const char *source)
Definition: digest.c:148
static void auth_digest_md5_to_ascii(unsigned char *source, unsigned char *dest)
Definition: digest.c:139


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:08