source: trunk/third/libsoup/libsoup/soup-auth-digest.c @ 21108

Revision 21108, 9.9 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21107, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-digest-offset: 8 -*- */
2/*
3 * soup-auth-digest.c: HTTP Digest Authentication
4 *
5 * Copyright (C) 2001-2003, Ximian, Inc.
6 */
7
8#ifdef HAVE_CONFIG_H
9#include <config.h>
10#endif
11
12#include <stdio.h>
13#include <string.h>
14#include <time.h>
15#include <unistd.h>
16
17#include "soup-auth-digest.h"
18#include "soup-headers.h"
19#include "soup-md5-utils.h"
20#include "soup-message.h"
21#include "soup-misc.h"
22#include "soup-uri.h"
23
24static void construct (SoupAuth *auth, const char *header);
25static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
26static const char *get_realm (SoupAuth *auth);
27static void authenticate (SoupAuth *auth, const char *username, const char *password);
28static gboolean is_authenticated (SoupAuth *auth);
29static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
30
31typedef enum {
32        QOP_NONE     = 0,
33        QOP_AUTH     = 1 << 0,
34        QOP_AUTH_INT = 1 << 1
35} QOPType;
36
37typedef enum {
38        ALGORITHM_MD5      = 1 << 0,
39        ALGORITHM_MD5_SESS = 1 << 1
40} AlgorithmType;
41
42struct SoupAuthDigestPrivate {
43        char          *user;
44        guchar         hex_a1[33];
45
46        /* These are provided by the server */
47        char          *realm;
48        char          *nonce;
49        QOPType        qop_options;
50        AlgorithmType  algorithm;
51        char          *domain;
52
53        /* These are generated by the client */
54        char          *cnonce;
55        int            nc;
56        QOPType        qop;
57};
58
59#define PARENT_TYPE SOUP_TYPE_AUTH
60static SoupAuthClass *parent_class;
61
62static void
63init (GObject *object)
64{
65        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (object);
66
67        digest->priv = g_new0 (SoupAuthDigestPrivate, 1);
68}
69
70static void
71finalize (GObject *object)
72{
73        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (object);
74
75        if (digest->priv->user)
76                g_free (digest->priv->user);
77        if (digest->priv->realm)
78                g_free (digest->priv->realm);
79        if (digest->priv->nonce)
80                g_free (digest->priv->nonce);
81        if (digest->priv->domain)
82                g_free (digest->priv->domain);
83        if (digest->priv->cnonce)
84                g_free (digest->priv->cnonce);
85
86        g_free (digest->priv);
87
88        G_OBJECT_CLASS (parent_class)->finalize (object);
89}
90
91static void
92class_init (GObjectClass *object_class)
93{
94        SoupAuthClass *auth_class = SOUP_AUTH_CLASS (object_class);
95
96        parent_class = g_type_class_ref (PARENT_TYPE);
97
98        auth_class->scheme_name = "Digest";
99
100        auth_class->get_protection_space = get_protection_space;
101        auth_class->get_realm = get_realm;
102        auth_class->construct = construct;
103        auth_class->authenticate = authenticate;
104        auth_class->is_authenticated = is_authenticated;
105        auth_class->get_authorization = get_authorization;
106
107        object_class->finalize = finalize;
108}
109
110SOUP_MAKE_TYPE (soup_auth_digest, SoupAuthDigest, class_init, init, PARENT_TYPE)
111
112typedef struct {
113        char *name;
114        guint type;
115} DataType;
116
117static DataType qop_types[] = {
118        { "auth",     QOP_AUTH     },
119        { "auth-int", QOP_AUTH_INT }
120};
121
122static DataType algorithm_types[] = {
123        { "MD5",      ALGORITHM_MD5      },
124        { "MD5-sess", ALGORITHM_MD5_SESS }
125};
126
127static guint
128decode_data_type (DataType *dtype, const char *name)
129{
130        int i;
131
132        if (!name)
133                return 0;
134
135        for (i = 0; dtype[i].name; i++) {
136                if (!g_strcasecmp (dtype[i].name, name))
137                        return dtype[i].type;
138        }
139
140        return 0;
141}
142
143static inline guint
144decode_qop (const char *name)
145{
146        return decode_data_type (qop_types, name);
147}
148
149static inline guint
150decode_algorithm (const char *name)
151{
152        return decode_data_type (algorithm_types, name);
153}
154
155static void
156construct (SoupAuth *auth, const char *header)
157{
158        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
159        GHashTable *tokens;
160        char *tmp, *ptr;
161
162        header += sizeof ("Digest");
163
164        tokens = soup_header_param_parse_list (header);
165        if (!tokens)
166                return;
167
168        digest->priv->nc = 1;
169        /* We're just going to do qop=auth for now */
170        digest->priv->qop = QOP_AUTH;
171
172        digest->priv->realm = soup_header_param_copy_token (tokens, "realm");
173        digest->priv->domain = soup_header_param_copy_token (tokens, "domain");
174        digest->priv->nonce = soup_header_param_copy_token (tokens, "nonce");
175
176        tmp = soup_header_param_copy_token (tokens, "qop");
177        ptr = tmp;
178
179        while (ptr && *ptr) {
180                char *token;
181
182                token = soup_header_param_decode_token ((char **)&ptr);
183                if (token)
184                        digest->priv->qop_options |= decode_qop (token);
185                g_free (token);
186
187                if (*ptr == ',')
188                        ptr++;
189        }
190        g_free (tmp);
191
192        tmp = soup_header_param_copy_token (tokens, "algorithm");
193        digest->priv->algorithm = decode_algorithm (tmp);
194        g_free (tmp);
195
196        soup_header_param_destroy_hash (tokens);
197}
198
199static GSList *
200get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
201{
202        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
203        GSList *space = NULL;
204        SoupUri *uri;
205        char *domain, *d, *lasts, *dir, *slash;
206
207        if (!digest->priv->domain || !*digest->priv->domain) {
208                /* If no domain directive, the protection space is the
209                 * whole server.
210                 */
211                return g_slist_prepend (NULL, g_strdup (""));
212        }
213
214        domain = g_strdup (digest->priv->domain);
215        for (d = strtok_r (domain, " ", &lasts); d; d = strtok_r (NULL, " ", &lasts)) {
216                if (*d == '/')
217                        dir = g_strdup (d);
218                else {
219                        uri = soup_uri_new (d);
220                        if (uri && uri->protocol == source_uri->protocol &&
221                            uri->port == source_uri->port &&
222                            !strcmp (uri->host, source_uri->host))
223                                dir = g_strdup (uri->path);
224                        else
225                                dir = NULL;
226                        if (uri)
227                                soup_uri_free (uri);
228                }
229
230                if (dir) {
231                        slash = strrchr (dir, '/');
232                        if (slash && !slash[1])
233                                *slash = '\0';
234
235                        space = g_slist_prepend (space, dir);
236                }
237        }
238        g_free (domain);
239
240        return space;
241}
242
243static const char *
244get_realm (SoupAuth *auth)
245{
246        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
247
248        return digest->priv->realm;
249}
250
251static void
252digest_hex (guchar *digest, guchar hex[33])
253{
254        guchar *s, *p;
255
256        /* lowercase hexify that bad-boy... */
257        for (s = digest, p = hex; p < hex + 32; s++, p += 2)
258                sprintf (p, "%.2x", *s);
259}
260
261static void
262authenticate (SoupAuth *auth, const char *username, const char *password)
263{
264        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
265        SoupMD5Context ctx;
266        guchar d[16];
267        char *bgen;
268
269        g_return_if_fail (username != NULL);
270
271        bgen = g_strdup_printf ("%p:%lu:%lu",
272                               auth,
273                               (unsigned long) getpid (),
274                               (unsigned long) time (0));
275        digest->priv->cnonce = soup_base64_encode (bgen, strlen (bgen));
276        g_free (bgen);
277
278        digest->priv->user = g_strdup (username);
279
280        /* compute A1 */
281        soup_md5_init (&ctx);
282
283        soup_md5_update (&ctx, username, strlen (username));
284
285        soup_md5_update (&ctx, ":", 1);
286        if (digest->priv->realm) {
287                soup_md5_update (&ctx, digest->priv->realm,
288                                 strlen (digest->priv->realm));
289        }
290
291        soup_md5_update (&ctx, ":", 1);
292        if (password)
293                soup_md5_update (&ctx, password, strlen (password));
294
295        if (digest->priv->algorithm == ALGORITHM_MD5_SESS) {
296                soup_md5_final (&ctx, d);
297
298                soup_md5_init (&ctx);
299                soup_md5_update (&ctx, d, 16);
300                soup_md5_update (&ctx, ":", 1);
301                soup_md5_update (&ctx, digest->priv->nonce,
302                                 strlen (digest->priv->nonce));
303                soup_md5_update (&ctx, ":", 1);
304                soup_md5_update (&ctx, digest->priv->cnonce,
305                                 strlen (digest->priv->cnonce));
306        }
307
308        /* hexify A1 */
309        soup_md5_final (&ctx, d);
310        digest_hex (d, digest->priv->hex_a1);
311}
312
313static gboolean
314is_authenticated (SoupAuth *auth)
315{
316        SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
317
318        return digest->priv->cnonce != NULL;
319}
320
321static char *
322compute_response (SoupAuthDigest *digest, SoupMessage *msg)
323{
324        guchar hex_a2[33], o[33];
325        guchar d[16];
326        SoupMD5Context md5;
327        char *url;
328        const SoupUri *uri;
329
330        uri = soup_message_get_uri (msg);
331        g_return_val_if_fail (uri != NULL, NULL);
332        url = soup_uri_to_string (uri, TRUE);
333
334        /* compute A2 */
335        soup_md5_init (&md5);
336        soup_md5_update (&md5, msg->method, strlen (msg->method));
337        soup_md5_update (&md5, ":", 1);
338        soup_md5_update (&md5, url, strlen (url));
339
340        g_free (url);
341
342        if (digest->priv->qop == QOP_AUTH_INT) {
343                /* FIXME: Actually implement. Ugh. */
344                soup_md5_update (&md5, ":", 1);
345                soup_md5_update (&md5, "00000000000000000000000000000000", 32);
346        }
347
348        /* now hexify A2 */
349        soup_md5_final (&md5, d);
350        digest_hex (d, hex_a2);
351
352        /* compute KD */
353        soup_md5_init (&md5);
354        soup_md5_update (&md5, digest->priv->hex_a1, 32);
355        soup_md5_update (&md5, ":", 1);
356        soup_md5_update (&md5, digest->priv->nonce,
357                         strlen (digest->priv->nonce));
358        soup_md5_update (&md5, ":", 1);
359
360        if (digest->priv->qop) {
361                char *tmp;
362
363                tmp = g_strdup_printf ("%.8x", digest->priv->nc);
364
365                soup_md5_update (&md5, tmp, strlen (tmp));
366                g_free (tmp);
367                soup_md5_update (&md5, ":", 1);
368                soup_md5_update (&md5, digest->priv->cnonce,
369                                 strlen (digest->priv->cnonce));
370                soup_md5_update (&md5, ":", 1);
371
372                if (digest->priv->qop == QOP_AUTH)
373                        tmp = "auth";
374                else if (digest->priv->qop == QOP_AUTH_INT)
375                        tmp = "auth-int";
376                else
377                        g_assert_not_reached ();
378
379                soup_md5_update (&md5, tmp, strlen (tmp));
380                soup_md5_update (&md5, ":", 1);
381        }
382
383        soup_md5_update (&md5, hex_a2, 32);
384        soup_md5_final (&md5, d);
385
386        digest_hex (d, o);
387
388        return g_strdup (o);
389}
390
391static char *
392get_authorization (SoupAuth *auth, SoupMessage *msg)
393{
394        SoupAuthDigest *digest = (SoupAuthDigest *) auth;
395        char *response;
396        char *qop = NULL;
397        char *nc;
398        char *url;
399        char *out;
400        const SoupUri *uri;
401
402        uri = soup_message_get_uri (msg);
403        g_return_val_if_fail (uri != NULL, NULL);
404        url = soup_uri_to_string (uri, TRUE);
405
406        response = compute_response (digest, msg);
407
408        if (digest->priv->qop == QOP_AUTH)
409                qop = "auth";
410        else if (digest->priv->qop == QOP_AUTH_INT)
411                qop = "auth-int";
412        else
413                g_assert_not_reached ();
414
415        nc = g_strdup_printf ("%.8x", digest->priv->nc);
416
417        out = g_strdup_printf (
418                "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", %s%s%s "
419                "%s%s%s %s%s%s uri=\"%s\", response=\"%s\"",
420                digest->priv->user,
421                digest->priv->realm,
422                digest->priv->nonce,
423
424                digest->priv->qop ? "cnonce=\"" : "",
425                digest->priv->qop ? digest->priv->cnonce : "",
426                digest->priv->qop ? "\"," : "",
427
428                digest->priv->qop ? "nc=" : "",
429                digest->priv->qop ? nc : "",
430                digest->priv->qop ? "," : "",
431
432                digest->priv->qop ? "qop=" : "",
433                digest->priv->qop ? qop : "",
434                digest->priv->qop ? "," : "",
435
436                url,
437                response);
438
439        g_free (response);
440        g_free (url);
441        g_free (nc);
442
443        digest->priv->nc++;
444
445        return out;
446}
Note: See TracBrowser for help on using the repository browser.