openssl: Fix uninitialized variable use in NPN callback
[platform/upstream/curl.git] / lib / http_digest.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
26
27 #include "urldata.h"
28 #include "rawstr.h"
29 #include "curl_base64.h"
30 #include "curl_md5.h"
31 #include "http_digest.h"
32 #include "strtok.h"
33 #include "curl_memory.h"
34 #include "vtls/vtls.h" /* for Curl_rand() */
35 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
36 #include "warnless.h"
37
38 #define _MPRINTF_REPLACE /* use our functions only */
39 #include <curl/mprintf.h>
40
41 /* The last #include file should be: */
42 #include "memdebug.h"
43
44 #define MAX_VALUE_LENGTH 256
45 #define MAX_CONTENT_LENGTH 1024
46
47 static void digest_cleanup_one(struct digestdata *dig);
48
49 /*
50  * Return 0 on success and then the buffers are filled in fine.
51  *
52  * Non-zero means failure to parse.
53  */
54 static int get_pair(const char *str, char *value, char *content,
55                     const char **endptr)
56 {
57   int c;
58   bool starts_with_quote = FALSE;
59   bool escape = FALSE;
60
61   for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); )
62     *value++ = *str++;
63   *value=0;
64
65   if('=' != *str++)
66     /* eek, no match */
67     return 1;
68
69   if('\"' == *str) {
70     /* this starts with a quote so it must end with one as well! */
71     str++;
72     starts_with_quote = TRUE;
73   }
74
75   for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) {
76     switch(*str) {
77     case '\\':
78       if(!escape) {
79         /* possibly the start of an escaped quote */
80         escape = TRUE;
81         *content++ = '\\'; /* even though this is an escape character, we still
82                               store it as-is in the target buffer */
83         continue;
84       }
85       break;
86     case ',':
87       if(!starts_with_quote) {
88         /* this signals the end of the content if we didn't get a starting
89            quote and then we do "sloppy" parsing */
90         c=0; /* the end */
91         continue;
92       }
93       break;
94     case '\r':
95     case '\n':
96       /* end of string */
97       c=0;
98       continue;
99     case '\"':
100       if(!escape && starts_with_quote) {
101         /* end of string */
102         c=0;
103         continue;
104       }
105       break;
106     }
107     escape = FALSE;
108     *content++ = *str;
109   }
110   *content=0;
111
112   *endptr = str;
113
114   return 0; /* all is fine! */
115 }
116
117 /* Test example headers:
118
119 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
120 Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
121
122 */
123
124 CURLdigest Curl_input_digest(struct connectdata *conn,
125                              bool proxy,
126                              const char *header) /* rest of the *-authenticate:
127                                                     header */
128 {
129   char *token = NULL;
130   char *tmp = NULL;
131   bool foundAuth = FALSE;
132   bool foundAuthInt = FALSE;
133   struct SessionHandle *data=conn->data;
134   bool before = FALSE; /* got a nonce before */
135   struct digestdata *d;
136
137   if(proxy) {
138     d = &data->state.proxydigest;
139   }
140   else {
141     d = &data->state.digest;
142   }
143
144   if(checkprefix("Digest", header)) {
145     header += strlen("Digest");
146
147     /* If we already have received a nonce, keep that in mind */
148     if(d->nonce)
149       before = TRUE;
150
151     /* clear off any former leftovers and init to defaults */
152     digest_cleanup_one(d);
153
154     for(;;) {
155       char value[MAX_VALUE_LENGTH];
156       char content[MAX_CONTENT_LENGTH];
157
158       while(*header && ISSPACE(*header))
159         header++;
160
161       /* extract a value=content pair */
162       if(!get_pair(header, value, content, &header)) {
163         if(Curl_raw_equal(value, "nonce")) {
164           d->nonce = strdup(content);
165           if(!d->nonce)
166             return CURLDIGEST_NOMEM;
167         }
168         else if(Curl_raw_equal(value, "stale")) {
169           if(Curl_raw_equal(content, "true")) {
170             d->stale = TRUE;
171             d->nc = 1; /* we make a new nonce now */
172           }
173         }
174         else if(Curl_raw_equal(value, "realm")) {
175           d->realm = strdup(content);
176           if(!d->realm)
177             return CURLDIGEST_NOMEM;
178         }
179         else if(Curl_raw_equal(value, "opaque")) {
180           d->opaque = strdup(content);
181           if(!d->opaque)
182             return CURLDIGEST_NOMEM;
183         }
184         else if(Curl_raw_equal(value, "qop")) {
185           char *tok_buf;
186           /* tokenize the list and choose auth if possible, use a temporary
187              clone of the buffer since strtok_r() ruins it */
188           tmp = strdup(content);
189           if(!tmp)
190             return CURLDIGEST_NOMEM;
191           token = strtok_r(tmp, ",", &tok_buf);
192           while(token != NULL) {
193             if(Curl_raw_equal(token, "auth")) {
194               foundAuth = TRUE;
195             }
196             else if(Curl_raw_equal(token, "auth-int")) {
197               foundAuthInt = TRUE;
198             }
199             token = strtok_r(NULL, ",", &tok_buf);
200           }
201           free(tmp);
202           /*select only auth o auth-int. Otherwise, ignore*/
203           if(foundAuth) {
204             d->qop = strdup("auth");
205             if(!d->qop)
206               return CURLDIGEST_NOMEM;
207           }
208           else if(foundAuthInt) {
209             d->qop = strdup("auth-int");
210             if(!d->qop)
211               return CURLDIGEST_NOMEM;
212           }
213         }
214         else if(Curl_raw_equal(value, "algorithm")) {
215           d->algorithm = strdup(content);
216           if(!d->algorithm)
217             return CURLDIGEST_NOMEM;
218           if(Curl_raw_equal(content, "MD5-sess"))
219             d->algo = CURLDIGESTALGO_MD5SESS;
220           else if(Curl_raw_equal(content, "MD5"))
221             d->algo = CURLDIGESTALGO_MD5;
222           else
223             return CURLDIGEST_BADALGO;
224         }
225         else {
226           /* unknown specifier, ignore it! */
227         }
228       }
229       else
230         break; /* we're done here */
231
232       /* pass all additional spaces here */
233       while(*header && ISSPACE(*header))
234         header++;
235       if(',' == *header)
236         /* allow the list to be comma-separated */
237         header++;
238     }
239     /* We had a nonce since before, and we got another one now without
240        'stale=true'. This means we provided bad credentials in the previous
241        request */
242     if(before && !d->stale)
243       return CURLDIGEST_BAD;
244
245     /* We got this header without a nonce, that's a bad Digest line! */
246     if(!d->nonce)
247       return CURLDIGEST_BAD;
248   }
249   else
250     /* else not a digest, get out */
251     return CURLDIGEST_NONE;
252
253   return CURLDIGEST_FINE;
254 }
255
256 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
257 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
258                          unsigned char *dest) /* 33 bytes */
259 {
260   int i;
261   for(i=0; i<16; i++)
262     snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
263 }
264
265 /* Perform quoted-string escaping as described in RFC2616 and its errata */
266 static char *string_quoted(const char *source)
267 {
268   char *dest, *d;
269   const char *s = source;
270   size_t n = 1; /* null terminator */
271
272   /* Calculate size needed */
273   while(*s) {
274     ++n;
275     if(*s == '"' || *s == '\\') {
276       ++n;
277     }
278     ++s;
279   }
280
281   dest = malloc(n);
282   if(dest) {
283     s = source;
284     d = dest;
285     while(*s) {
286       if(*s == '"' || *s == '\\') {
287         *d++ = '\\';
288       }
289       *d++ = *s++;
290     }
291     *d = 0;
292   }
293
294   return dest;
295 }
296
297 CURLcode Curl_output_digest(struct connectdata *conn,
298                             bool proxy,
299                             const unsigned char *request,
300                             const unsigned char *uripath)
301 {
302   /* We have a Digest setup for this, use it!  Now, to get all the details for
303      this sorted out, I must urge you dear friend to read up on the RFC2617
304      section 3.2.2, */
305   size_t urilen;
306   unsigned char md5buf[16]; /* 16 bytes/128 bits */
307   unsigned char request_digest[33];
308   unsigned char *md5this;
309   unsigned char ha1[33];/* 32 digits and 1 zero byte */
310   unsigned char ha2[33];/* 32 digits and 1 zero byte */
311   char cnoncebuf[33];
312   char *cnonce = NULL;
313   size_t cnonce_sz = 0;
314   char *tmp = NULL;
315   char **allocuserpwd;
316   size_t userlen;
317   const char *userp;
318   char *userp_quoted;
319   const char *passwdp;
320   struct auth *authp;
321
322   struct SessionHandle *data = conn->data;
323   struct digestdata *d;
324   CURLcode rc;
325 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
326    It converts digest text to ASCII so the MD5 will be correct for
327    what ultimately goes over the network.
328 */
329 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
330   rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
331   if(rc != CURLE_OK) { \
332     free(b); \
333     return rc; \
334   }
335
336   if(proxy) {
337     d = &data->state.proxydigest;
338     allocuserpwd = &conn->allocptr.proxyuserpwd;
339     userp = conn->proxyuser;
340     passwdp = conn->proxypasswd;
341     authp = &data->state.authproxy;
342   }
343   else {
344     d = &data->state.digest;
345     allocuserpwd = &conn->allocptr.userpwd;
346     userp = conn->user;
347     passwdp = conn->passwd;
348     authp = &data->state.authhost;
349   }
350
351   Curl_safefree(*allocuserpwd);
352
353   /* not set means empty */
354   if(!userp)
355     userp="";
356
357   if(!passwdp)
358     passwdp="";
359
360   if(!d->nonce) {
361     authp->done = FALSE;
362     return CURLE_OK;
363   }
364   authp->done = TRUE;
365
366   if(!d->nc)
367     d->nc = 1;
368
369   if(!d->cnonce) {
370     struct timeval now = Curl_tvnow();
371     snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x",
372              Curl_rand(data), Curl_rand(data),
373              (unsigned int)now.tv_sec,
374              (unsigned int)now.tv_usec);
375
376     rc = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
377                             &cnonce, &cnonce_sz);
378     if(rc)
379       return rc;
380     d->cnonce = cnonce;
381   }
382
383   /*
384     if the algorithm is "MD5" or unspecified (which then defaults to MD5):
385
386     A1 = unq(username-value) ":" unq(realm-value) ":" passwd
387
388     if the algorithm is "MD5-sess" then:
389
390     A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
391          ":" unq(nonce-value) ":" unq(cnonce-value)
392   */
393
394   md5this = (unsigned char *)
395     aprintf("%s:%s:%s", userp, d->realm, passwdp);
396   if(!md5this)
397     return CURLE_OUT_OF_MEMORY;
398
399   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
400   Curl_md5it(md5buf, md5this);
401   Curl_safefree(md5this);
402   md5_to_ascii(md5buf, ha1);
403
404   if(d->algo == CURLDIGESTALGO_MD5SESS) {
405     /* nonce and cnonce are OUTSIDE the hash */
406     tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
407     if(!tmp)
408       return CURLE_OUT_OF_MEMORY;
409     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
410     Curl_md5it(md5buf, (unsigned char *)tmp);
411     Curl_safefree(tmp);
412     md5_to_ascii(md5buf, ha1);
413   }
414
415   /*
416     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
417
418       A2       = Method ":" digest-uri-value
419
420           If the "qop" value is "auth-int", then A2 is:
421
422       A2       = Method ":" digest-uri-value ":" H(entity-body)
423
424     (The "Method" value is the HTTP request method as specified in section
425     5.1.1 of RFC 2616)
426   */
427
428   /* So IE browsers < v7 cut off the URI part at the query part when they
429      evaluate the MD5 and some (IIS?) servers work with them so we may need to
430      do the Digest IE-style. Note that the different ways cause different MD5
431      sums to get sent.
432
433      Apache servers can be set to do the Digest IE-style automatically using
434      the BrowserMatch feature:
435      http://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie
436
437      Further details on Digest implementation differences:
438      http://www.fngtps.com/2006/09/http-authentication
439   */
440
441   if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL))
442     urilen = tmp - (char *)uripath;
443   else
444     urilen = strlen((char *)uripath);
445
446   md5this = (unsigned char *)aprintf("%s:%.*s", request, urilen, uripath);
447
448   if(d->qop && Curl_raw_equal(d->qop, "auth-int")) {
449     /* We don't support auth-int for PUT or POST at the moment.
450        TODO: replace md5 of empty string with entity-body for PUT/POST */
451     unsigned char *md5this2 = (unsigned char *)
452       aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
453     Curl_safefree(md5this);
454     md5this = md5this2;
455   }
456
457   if(!md5this)
458     return CURLE_OUT_OF_MEMORY;
459
460   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
461   Curl_md5it(md5buf, md5this);
462   Curl_safefree(md5this);
463   md5_to_ascii(md5buf, ha2);
464
465   if(d->qop) {
466     md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
467                                        ha1,
468                                        d->nonce,
469                                        d->nc,
470                                        d->cnonce,
471                                        d->qop,
472                                        ha2);
473   }
474   else {
475     md5this = (unsigned char *)aprintf("%s:%s:%s",
476                                        ha1,
477                                        d->nonce,
478                                        ha2);
479   }
480   if(!md5this)
481     return CURLE_OUT_OF_MEMORY;
482
483   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
484   Curl_md5it(md5buf, md5this);
485   Curl_safefree(md5this);
486   md5_to_ascii(md5buf, request_digest);
487
488   /* for test case 64 (snooped from a Mozilla 1.3a request)
489
490     Authorization: Digest username="testuser", realm="testrealm", \
491     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
492
493     Digest parameters are all quoted strings.  Username which is provided by
494     the user will need double quotes and backslashes within it escaped.  For
495     the other fields, this shouldn't be an issue.  realm, nonce, and opaque
496     are copied as is from the server, escapes and all.  cnonce is generated
497     with web-safe characters.  uri is already percent encoded.  nc is 8 hex
498     characters.  algorithm and qop with standard values only contain web-safe
499     chracters.
500   */
501   userp_quoted = string_quoted(userp);
502   if(!userp_quoted)
503     return CURLE_OUT_OF_MEMORY;
504
505   if(d->qop) {
506     *allocuserpwd =
507       aprintf( "%sAuthorization: Digest "
508                "username=\"%s\", "
509                "realm=\"%s\", "
510                "nonce=\"%s\", "
511                "uri=\"%.*s\", "
512                "cnonce=\"%s\", "
513                "nc=%08x, "
514                "qop=%s, "
515                "response=\"%s\"",
516                proxy?"Proxy-":"",
517                userp_quoted,
518                d->realm,
519                d->nonce,
520                urilen, uripath, /* this is the PATH part of the URL */
521                d->cnonce,
522                d->nc,
523                d->qop,
524                request_digest);
525
526     if(Curl_raw_equal(d->qop, "auth"))
527       d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
528                   which tells to the server how many times you are using the
529                   same nonce in the qop=auth mode. */
530   }
531   else {
532     *allocuserpwd =
533       aprintf( "%sAuthorization: Digest "
534                "username=\"%s\", "
535                "realm=\"%s\", "
536                "nonce=\"%s\", "
537                "uri=\"%.*s\", "
538                "response=\"%s\"",
539                proxy?"Proxy-":"",
540                userp_quoted,
541                d->realm,
542                d->nonce,
543                urilen, uripath, /* this is the PATH part of the URL */
544                request_digest);
545   }
546   Curl_safefree(userp_quoted);
547   if(!*allocuserpwd)
548     return CURLE_OUT_OF_MEMORY;
549
550   /* Add optional fields */
551   if(d->opaque) {
552     /* append opaque */
553     tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
554     if(!tmp)
555       return CURLE_OUT_OF_MEMORY;
556     free(*allocuserpwd);
557     *allocuserpwd = tmp;
558   }
559
560   if(d->algorithm) {
561     /* append algorithm */
562     tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
563     if(!tmp)
564       return CURLE_OUT_OF_MEMORY;
565     free(*allocuserpwd);
566     *allocuserpwd = tmp;
567   }
568
569   /* append CRLF + zero (3 bytes) to the userpwd header */
570   userlen = strlen(*allocuserpwd);
571   tmp = realloc(*allocuserpwd, userlen + 3);
572   if(!tmp)
573     return CURLE_OUT_OF_MEMORY;
574   strcpy(&tmp[userlen], "\r\n"); /* append the data */
575   *allocuserpwd = tmp;
576
577   return CURLE_OK;
578 }
579
580 static void digest_cleanup_one(struct digestdata *d)
581 {
582   Curl_safefree(d->nonce);
583   Curl_safefree(d->cnonce);
584   Curl_safefree(d->realm);
585   Curl_safefree(d->opaque);
586   Curl_safefree(d->qop);
587   Curl_safefree(d->algorithm);
588
589   d->nc = 0;
590   d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
591   d->stale = FALSE; /* default means normal, not stale */
592 }
593
594
595 void Curl_digest_cleanup(struct SessionHandle *data)
596 {
597   digest_cleanup_one(&data->state.digest);
598   digest_cleanup_one(&data->state.proxydigest);
599 }
600
601 #endif