Initial Digest support. At least partly working.
[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     data->state.digest.algo = CURLDIGESTALGO_MD5; /* default algorithm */
69
70     while(more) {
71       char value[32];
72       char content[128];
73       int totlen=0;
74
75       while(*header && isspace((int)*header))
76         header++;
77     
78       /* how big can these strings be? */
79       if(2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
80                      value, content)) {
81         if(strequal(value, "nonce")) {
82           data->state.digest.nonce = strdup(content);
83         }
84         else if(strequal(value, "cnonce")) {
85           data->state.digest.cnonce = strdup(content);
86         }
87         else if(strequal(value, "realm")) {
88           data->state.digest.realm = strdup(content);
89         }
90         else if(strequal(value, "algorithm")) {
91           if(strequal(content, "MD5-sess"))
92             data->state.digest.algo = CURLDIGESTALGO_MD5SESS;
93           /* else, remain using the default md5 */
94         }
95         else {
96           /* unknown specifier, ignore it! */
97         }
98         totlen = strlen(value)+strlen(content)+3;
99       }
100       else 
101         break; /* we're done here */
102         
103       header += totlen;
104       if(',' == *header)
105         /* allow the list to be comma-separated */
106         header++; 
107     }
108   }
109   else 
110     /* else not a digest, get out */
111     return CURLDIGEST_NONE;
112
113   return CURLDIGEST_FINE;
114 }
115
116 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
117 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
118                          unsigned char *dest) /* 33 bytes */
119 {
120   int i;
121   for(i=0; i<16; i++)
122     sprintf((char *)&dest[i*2], "%02x", source[i]);
123 }
124
125 CURLcode Curl_output_digest(struct connectdata *conn,
126                             unsigned char *request,
127                             unsigned char *uripath)
128 {
129   /* We have a Digest setup for this, use it!
130      Now, to get all the details for this sorted out, I must urge you dear friend
131      to read up on the RFC2617 section 3.2.2, */
132   unsigned char md5buf[16]; /* 16 bytes/128 bits */
133   unsigned char ha1[33]; /* 32 digits and 1 zero byte */
134   unsigned char ha2[33];
135   unsigned char request_digest[33];
136   char *md5this;
137
138   struct SessionHandle *data = conn->data;
139
140   /*
141     if the algorithm is "MD5" or unspecified (which then defaults to MD5):
142     
143     A1 = unq(username-value) ":" unq(realm-value) ":" passwd
144
145     if the algorithm is "MD5-sess" then:
146
147     A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
148          ":" unq(nonce-value) ":" unq(cnonce-value)
149   */
150   if(data->state.digest.algo == CURLDIGESTALGO_MD5SESS) {
151     md5this = aprintf("%s:%s:%s:%s:%s",
152                       data->state.user,
153                       data->state.digest.realm,
154                       data->state.passwd,
155                       data->state.digest.nonce,
156                       data->state.digest.cnonce);
157   }
158   else {
159     md5this = aprintf("%s:%s:%s",
160                       data->state.user,
161                       data->state.digest.realm,
162                       data->state.passwd);
163   }
164   Curl_md5it(md5buf, md5this);
165   free(md5this); /* free this again */
166   md5_to_ascii(md5buf, ha1);
167
168   /*
169     A2 = Method ":" digest-uri-value
170     
171     (The "Method" value is the HTTP request method as specified in section
172     5.1.1 of RFC 2616)
173   */
174
175   md5this = aprintf("%s:%s", request, uripath);
176   Curl_md5it(md5buf, md5this);
177   free(md5this); /* free this again */
178   md5_to_ascii(md5buf, ha2);
179
180   md5this = aprintf("%s:%s:%s",
181                     ha1,
182                     data->state.digest.nonce,
183                     ha2);
184   Curl_md5it(md5buf, md5this);
185   free(md5this); /* free this again */
186   md5_to_ascii(md5buf, request_digest);
187
188   /* for test case 64 (snooped from a Mozilla 1.3a request)
189
190     Authorization: Digest username="testuser", realm="testrealm", \
191     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
192   */
193
194   conn->allocptr.userpwd =
195     aprintf( "Authorization: Digest "
196              "username=\"%s\", "
197              "realm=\"%s\", "
198              "nonce=\"%s\", "
199              "uri=\"%s\", "
200              "response=\"%s\"\r\n",
201              data->state.user,
202              data->state.digest.realm,
203              data->state.digest.nonce,
204              uripath, /* this is the PATH part of the URL */ 
205              request_digest );
206
207   return CURLE_OK;
208 }
209
210 #endif