Better Digest stuff
[platform/upstream/curl.git] / lib / http_digest.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2003, 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
37 #include "md5.h"
38 #include "http_digest.h"
39
40 #define _MPRINTF_REPLACE /* use our functions only */
41 #include <curl/mprintf.h>
42
43 /* The last #include file should be: */
44 #ifdef MALLOCDEBUG
45 #include "memdebug.h"
46 #endif
47
48 /* Test example header:
49
50 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
51
52 */
53
54 CURLdigest Curl_input_digest(struct connectdata *conn,
55                              char *header) /* rest of the www-authenticate:
56                                               header */
57 {
58   bool more = TRUE;
59   struct SessionHandle *data=conn->data;
60
61   /* skip initial whitespaces */
62   while(*header && isspace((int)*header))
63     header++;
64
65   if(checkprefix("Digest", header)) {
66     header += strlen("Digest");
67
68     /* clear off any former leftovers and init to defaults */
69     Curl_digest_cleanup(data);
70
71     while(more) {
72       char value[32];
73       char content[128];
74       int totlen=0;
75
76       while(*header && isspace((int)*header))
77         header++;
78     
79       /* how big can these strings be? */
80       if(2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
81                      value, content)) {
82         if(strequal(value, "nonce")) {
83           data->state.digest.nonce = strdup(content);
84         }
85         else if(strequal(value, "cnonce")) {
86           data->state.digest.cnonce = strdup(content);
87         }
88         else if(strequal(value, "realm")) {
89           data->state.digest.realm = strdup(content);
90         }
91         else if(strequal(value, "algorithm")) {
92           if(strequal(content, "MD5-sess"))
93             data->state.digest.algo = CURLDIGESTALGO_MD5SESS;
94           /* else, remain using the default md5 */
95         }
96         else {
97           /* unknown specifier, ignore it! */
98         }
99         totlen = strlen(value)+strlen(content)+3;
100       }
101       else 
102         break; /* we're done here */
103         
104       header += totlen;
105       if(',' == *header)
106         /* allow the list to be comma-separated */
107         header++; 
108     }
109   }
110   else 
111     /* else not a digest, get out */
112     return CURLDIGEST_NONE;
113
114   return CURLDIGEST_FINE;
115 }
116
117 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
118 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
119                          unsigned char *dest) /* 33 bytes */
120 {
121   int i;
122   for(i=0; i<16; i++)
123     sprintf((char *)&dest[i*2], "%02x", source[i]);
124 }
125
126 CURLcode Curl_output_digest(struct connectdata *conn,
127                             unsigned char *request,
128                             unsigned char *uripath)
129 {
130   /* We have a Digest setup for this, use it!
131      Now, to get all the details for this sorted out, I must urge you dear friend
132      to read up on the RFC2617 section 3.2.2, */
133   unsigned char md5buf[16]; /* 16 bytes/128 bits */
134   unsigned char ha1[33]; /* 32 digits and 1 zero byte */
135   unsigned char ha2[33];
136   unsigned char request_digest[33];
137   unsigned char *md5this;
138
139   struct SessionHandle *data = conn->data;
140
141   /*
142     if the algorithm is "MD5" or unspecified (which then defaults to MD5):
143     
144     A1 = unq(username-value) ":" unq(realm-value) ":" passwd
145
146     if the algorithm is "MD5-sess" then:
147
148     A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
149          ":" unq(nonce-value) ":" unq(cnonce-value)
150   */
151   if(data->state.digest.algo == CURLDIGESTALGO_MD5SESS) {
152     md5this = (unsigned char *)
153       aprintf("%s:%s:%s:%s:%s",
154               data->state.user,
155               data->state.digest.realm,
156               data->state.passwd,
157               data->state.digest.nonce,
158               data->state.digest.cnonce);
159   }
160   else {
161     md5this = (unsigned char *)
162       aprintf("%s:%s:%s",
163               data->state.user,
164               data->state.digest.realm,
165               data->state.passwd);
166   }
167   Curl_md5it(md5buf, md5this);
168   free(md5this); /* free this again */
169   md5_to_ascii(md5buf, ha1);
170
171   /*
172     A2 = Method ":" digest-uri-value
173     
174     (The "Method" value is the HTTP request method as specified in section
175     5.1.1 of RFC 2616)
176   */
177
178   md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
179   Curl_md5it(md5buf, md5this);
180   free(md5this); /* free this again */
181   md5_to_ascii(md5buf, ha2);
182   
183   md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, data->state.digest.nonce,
184                                      ha2);
185   Curl_md5it(md5buf, md5this);
186   free(md5this); /* free this again */
187   md5_to_ascii(md5buf, request_digest);
188
189   /* for test case 64 (snooped from a Mozilla 1.3a request)
190
191     Authorization: Digest username="testuser", realm="testrealm", \
192     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
193   */
194
195   conn->allocptr.userpwd =
196     aprintf( "Authorization: Digest "
197              "username=\"%s\", "
198              "realm=\"%s\", "
199              "nonce=\"%s\", "
200              "uri=\"%s\", "
201              "response=\"%s\"\r\n",
202              data->state.user,
203              data->state.digest.realm,
204              data->state.digest.nonce,
205              uripath, /* this is the PATH part of the URL */ 
206              request_digest );
207
208   return CURLE_OK;
209 }
210
211 void Curl_digest_cleanup(struct SessionHandle *data)
212 {
213   if(data->state.digest.nonce)
214     free(data->state.digest.nonce);
215   data->state.digest.nonce = NULL;
216
217   if(data->state.digest.cnonce)
218     free(data->state.digest.cnonce);
219   data->state.digest.cnonce = NULL;
220
221   if(data->state.digest.realm)
222     free(data->state.digest.realm);
223   data->state.digest.realm = NULL;
224
225   data->state.digest.algo = CURLDIGESTALGO_MD5; /* default algorithm */
226 }
227
228 #endif