Check that memory functions return non-NULL or return error.
[platform/upstream/curl.git] / lib / http_digest.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, 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  * $Id$
22  ***************************************************************************/
23 #include "setup.h"
24
25 #ifndef CURL_DISABLE_HTTP
26 /* -- WIN32 approved -- */
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32
33 #include "urldata.h"
34 #include "sendf.h"
35 #include "strequal.h"
36 #include "base64.h"
37 #include "md5.h"
38 #include "http_digest.h"
39 #include "strtok.h"
40 #include "url.h" /* for Curl_safefree() */
41 #include "memory.h"
42
43 #define _MPRINTF_REPLACE /* use our functions only */
44 #include <curl/mprintf.h>
45
46 /* The last #include file should be: */
47 #include "memdebug.h"
48
49 /* Test example headers:
50
51 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
52 Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
53
54 */
55
56 CURLdigest Curl_input_digest(struct connectdata *conn,
57                              bool proxy,
58                              char *header) /* rest of the *-authenticate:
59                                               header */
60 {
61   bool more = TRUE;
62   char *token = NULL;
63   char *tmp = NULL;
64   bool foundAuth = FALSE;
65   bool foundAuthInt = FALSE;
66   struct SessionHandle *data=conn->data;
67   bool before = FALSE; /* got a nonce before */
68   struct digestdata *d;
69   
70   if(proxy) {
71     d = &data->state.proxydigest;
72   }
73   else {
74     d = &data->state.digest;
75   }
76
77   /* skip initial whitespaces */
78   while(*header && isspace((int)*header))
79     header++;
80
81   if(checkprefix("Digest", header)) {
82     header += strlen("Digest");
83
84     /* If we already have received a nonce, keep that in mind */
85     if(d->nonce)
86       before = TRUE;
87
88     /* clear off any former leftovers and init to defaults */
89     Curl_digest_cleanup_one(d);
90
91     while(more) {
92       char value[32];
93       char content[128];
94       size_t totlen=0;
95
96       while(*header && isspace((int)*header))
97         header++;
98
99       /* how big can these strings be? */
100       if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
101                       value, content)) ||
102          /* try the same scan but without quotes around the content but don't
103             include the possibly trailing comma */
104          (2 ==  sscanf(header, "%31[^=]=%127[^,]",
105                        value, content)) ) {
106         if(strequal(value, "nonce")) {
107           d->nonce = strdup(content);
108         }
109         else if(strequal(value, "stale")) {
110           if(strequal(content, "true")) {
111             d->stale = TRUE;
112             d->nc = 1; /* we make a new nonce now */
113           }
114         }
115         else if(strequal(value, "realm")) {
116           d->realm = strdup(content);
117         }
118         else if(strequal(value, "opaque")) {
119           d->opaque = strdup(content);
120         }
121         else if(strequal(value, "qop")) {
122           char *tok_buf;
123           /* tokenize the list and choose auth if possible, use a temporary
124              clone of the buffer since strtok_r() ruins it */
125           tmp = strdup(content);
126           token = strtok_r(tmp, ",", &tok_buf);
127           while (token != NULL) {
128             if (strequal(token, "auth")) {
129               foundAuth = TRUE;
130             }
131             else if (strequal(token, "auth-int")) {
132               foundAuthInt = TRUE;
133             }
134             token = strtok_r(NULL, ",", &tok_buf);
135           }
136           free(tmp);
137           /*select only auth o auth-int. Otherwise, ignore*/
138           if (foundAuth) {
139             d->qop = strdup("auth");
140           }
141           else if (foundAuthInt) {
142             d->qop = strdup("auth-int");
143           }
144         }
145         else if(strequal(value, "algorithm")) {
146           d->algorithm = strdup(content);
147           if(strequal(content, "MD5-sess"))
148             d->algo = CURLDIGESTALGO_MD5SESS;
149           else if(strequal(content, "MD5"))
150             d->algo = CURLDIGESTALGO_MD5;
151           else
152             return CURLDIGEST_BADALGO;
153         }
154         else {
155           /* unknown specifier, ignore it! */
156         }
157         totlen = strlen(value)+strlen(content)+3;
158       }
159       else
160         break; /* we're done here */
161
162       header += totlen;
163       if(',' == *header)
164         /* allow the list to be comma-separated */
165         header++;
166     }
167     /* We had a nonce since before, and we got another one now without
168        'stale=true'. This means we provided bad credentials in the previous
169        request */
170     if(before && !d->stale)
171       return CURLDIGEST_BAD;
172
173     /* We got this header without a nonce, that's a bad Digest line! */
174     if(!d->nonce)
175       return CURLDIGEST_BAD;
176   }
177   else
178     /* else not a digest, get out */
179     return CURLDIGEST_NONE;
180
181   return CURLDIGEST_FINE;
182 }
183
184 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
185 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
186                          unsigned char *dest) /* 33 bytes */
187 {
188   int i;
189   for(i=0; i<16; i++)
190     sprintf((char *)&dest[i*2], "%02x", source[i]);
191 }
192
193 CURLcode Curl_output_digest(struct connectdata *conn,
194                             bool proxy,
195                             unsigned char *request,
196                             unsigned char *uripath)
197 {
198   /* We have a Digest setup for this, use it!  Now, to get all the details for
199      this sorted out, I must urge you dear friend to read up on the RFC2617
200      section 3.2.2, */
201   unsigned char md5buf[16]; /* 16 bytes/128 bits */
202   unsigned char request_digest[33];
203   unsigned char *md5this;
204   unsigned char *ha1;
205   unsigned char ha2[33];/* 32 digits and 1 zero byte */
206   char cnoncebuf[7];
207   char *cnonce;
208   char *tmp = NULL;
209   struct timeval now;
210   struct auth *authp;
211   char **userp;
212
213   struct SessionHandle *data = conn->data;
214   struct digestdata *d;
215
216   if(proxy) {
217     d = &data->state.proxydigest;
218     authp = &data->state.authproxy;
219     userp = &conn->allocptr.proxyuserpwd;
220   }
221   else {
222     d = &data->state.digest;
223     authp = &data->state.authhost;
224     userp = &conn->allocptr.userpwd;
225   }
226
227   if(!d->nonce) {
228     authp->done = FALSE;
229     return CURLE_OK;
230   }
231   authp->done = TRUE;
232
233   if(!d->nc)
234     d->nc = 1;
235
236   if(!d->cnonce) {
237     /* Generate a cnonce */
238     now = Curl_tvnow();
239     snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec);
240     if(Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), &cnonce))
241       d->cnonce = cnonce;
242     else
243       return CURLE_OUT_OF_MEMORY;
244   }
245
246   /*
247     if the algorithm is "MD5" or unspecified (which then defaults to MD5):
248
249     A1 = unq(username-value) ":" unq(realm-value) ":" passwd
250
251     if the algorithm is "MD5-sess" then:
252
253     A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
254          ":" unq(nonce-value) ":" unq(cnonce-value)
255   */
256
257   md5this = (unsigned char *)
258     aprintf("%s:%s:%s", conn->user, d->realm, conn->passwd);
259   if(!md5this)
260     return CURLE_OUT_OF_MEMORY;
261   Curl_md5it(md5buf, md5this);
262   free(md5this); /* free this again */
263
264   ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
265   if(!ha1)
266     return CURLE_OUT_OF_MEMORY;
267
268   md5_to_ascii(md5buf, ha1);
269
270   if(d->algo == CURLDIGESTALGO_MD5SESS) {
271     /* nonce and cnonce are OUTSIDE the hash */
272     tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
273     free(ha1);
274     if(!tmp)
275       return CURLE_OUT_OF_MEMORY;
276     ha1 = (unsigned char *)tmp;
277   }
278
279   /*
280     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
281
282       A2       = Method ":" digest-uri-value
283
284           If the "qop" value is "auth-int", then A2 is:
285
286       A2       = Method ":" digest-uri-value ":" H(entity-body)
287
288     (The "Method" value is the HTTP request method as specified in section
289     5.1.1 of RFC 2616)
290   */
291
292   md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
293   if(!md5this) {
294     free(ha1);
295     return CURLE_OUT_OF_MEMORY;
296   }
297
298   if (d->qop && strequal(d->qop, "auth-int")) {
299     /* We don't support auth-int at the moment. I can't see a easy way to get
300        entity-body here */
301     /* TODO: Append H(entity-body)*/
302   }
303   Curl_md5it(md5buf, md5this);
304   free(md5this); /* free this again */
305   md5_to_ascii(md5buf, ha2);
306
307   if (d->qop) {
308     md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
309                                        ha1,
310                                        d->nonce,
311                                        d->nc,
312                                        d->cnonce,
313                                        d->qop,
314                                        ha2);
315   }
316   else {
317     md5this = (unsigned char *)aprintf("%s:%s:%s",
318                                        ha1,
319                                        d->nonce,
320                                        ha2);
321   }
322   free(ha1);
323   if(!md5this)
324     return CURLE_OUT_OF_MEMORY;
325
326   Curl_md5it(md5buf, md5this);
327   free(md5this); /* free this again */
328   md5_to_ascii(md5buf, request_digest);
329
330   /* for test case 64 (snooped from a Mozilla 1.3a request)
331
332     Authorization: Digest username="testuser", realm="testrealm", \
333     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
334   */
335
336   Curl_safefree(conn->allocptr.userpwd);
337
338   if (d->qop) {
339     *userp =
340       aprintf( "%sAuthorization: Digest "
341                "username=\"%s\", "
342                "realm=\"%s\", "
343                "nonce=\"%s\", "
344                "uri=\"%s\", "
345                "cnonce=\"%s\", "
346                "nc=%08x, "
347                "qop=\"%s\", "
348                "response=\"%s\"",
349                proxy?"Proxy-":"",
350                conn->user,
351                d->realm,
352                d->nonce,
353                uripath, /* this is the PATH part of the URL */
354                d->cnonce,
355                d->nc,
356                d->qop,
357                request_digest);
358
359     if(strequal(d->qop, "auth"))
360       d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
361                   which tells to the server how many times you are using the
362                   same nonce in the qop=auth mode. */
363   }
364   else {
365     *userp =
366       aprintf( "%sAuthorization: Digest "
367                "username=\"%s\", "
368                "realm=\"%s\", "
369                "nonce=\"%s\", "
370                "uri=\"%s\", "
371                "response=\"%s\"",
372                proxy?"Proxy-":"",
373                conn->user,
374                d->realm,
375                d->nonce,
376                uripath, /* this is the PATH part of the URL */
377                request_digest);
378   }
379   if(!*userp)
380     return CURLE_OUT_OF_MEMORY;
381
382   /* Add optional fields */
383   if(d->opaque) {
384     /* append opaque */
385     tmp = aprintf("%s, opaque=\"%s\"", *userp, d->opaque);
386     if(!tmp)
387       return CURLE_OUT_OF_MEMORY;
388     free(*userp);
389     *userp = tmp;
390   }
391
392   if(d->algorithm) {
393     /* append algorithm */
394     tmp = aprintf("%s, algorithm=\"%s\"", *userp, d->algorithm);
395     if(!tmp)
396       return CURLE_OUT_OF_MEMORY;
397     free(*userp);
398     *userp = tmp;
399   }
400
401   /* append CRLF to the userpwd header */
402   tmp = (char*) realloc(*userp, strlen(*userp) + 3 + 1);
403   if(!tmp)
404     return CURLE_OUT_OF_MEMORY;
405   strcat(tmp, "\r\n");
406   *userp = tmp;
407
408   return CURLE_OK;
409 }
410
411 void Curl_digest_cleanup_one(struct digestdata *d)
412 {
413   if(d->nonce)
414     free(d->nonce);
415   d->nonce = NULL;
416
417   if(d->cnonce)
418     free(d->cnonce);
419   d->cnonce = NULL;
420
421   if(d->realm)
422     free(d->realm);
423   d->realm = NULL;
424
425   if(d->opaque)
426     free(d->opaque);
427   d->opaque = NULL;
428
429   if(d->qop)
430     free(d->qop);
431   d->qop = NULL;
432
433   if(d->algorithm)
434     free(d->algorithm);
435   d->algorithm = NULL;
436
437   d->nc = 0;
438   d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
439   d->stale = FALSE; /* default means normal, not stale */
440 }
441
442
443 void Curl_digest_cleanup(struct SessionHandle *data)
444 {
445   Curl_digest_cleanup_one(&data->state.digest);
446   Curl_digest_cleanup_one(&data->state.proxydigest);
447 }
448
449 #endif