~ubuntu-branches/ubuntu/feisty/elinks/feisty-updates

« back to all changes in this revision

Viewing changes to src/protocol/auth/auth.c

  • Committer: Bazaar Package Importer
  • Author(s): Peter Gervai
  • Date: 2004-01-21 22:13:45 UTC
  • Revision ID: james.westby@ubuntu.com-20040121221345-ju33hai1yhhqt6kn
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* HTTP Authentication support */
 
2
/* $Id: auth.c,v 1.70 2003/12/21 14:51:20 zas Exp $ */
 
3
 
 
4
#ifdef HAVE_CONFIG_H
 
5
#include "config.h"
 
6
#endif
 
7
 
 
8
#include <string.h>
 
9
 
 
10
#include "elinks.h"
 
11
 
 
12
#include "intl/gettext/libintl.h"
 
13
#include "protocol/auth/auth.h"
 
14
#include "protocol/auth/dialogs.h"
 
15
#include "protocol/protocol.h"
 
16
#include "protocol/uri.h"
 
17
#include "sched/session.h"
 
18
#include "util/base64.h"
 
19
#include "util/error.h"
 
20
#include "util/memory.h"
 
21
#include "util/string.h"
 
22
 
 
23
/* Defines to 1 to enable http auth debugging output. */
 
24
#if 0
 
25
#define DEBUG_HTTP_AUTH
 
26
#endif
 
27
 
 
28
static INIT_LIST_HEAD(http_auth_basic_list);
 
29
 
 
30
 
 
31
/* Find if url/realm is in auth list. If a matching url is found, but realm is
 
32
 * NULL, it returns the first record found. If realm isn't NULL, it returns
 
33
 * the first record that matches exactly (url and realm) if any. */
 
34
static struct http_auth_basic *
 
35
find_auth_entry(unsigned char *url, unsigned char *realm)
 
36
{
 
37
        struct http_auth_basic *match = NULL, *entry;
 
38
 
 
39
#ifdef DEBUG_HTTP_AUTH
 
40
        DBG("find_auth_entry: url=%s realm=%s", url, realm);
 
41
#endif
 
42
 
 
43
        if (!url || !*url) return NULL;
 
44
 
 
45
        foreach (entry, http_auth_basic_list) {
 
46
                if (strcasecmp(entry->url, url)) continue;
 
47
 
 
48
                /* Found a matching url. */
 
49
                match = entry;
 
50
                if (!realm) {
 
51
                        /* Since realm is NULL, stops immediatly. */
 
52
                        break;
 
53
                }
 
54
 
 
55
                /* From RFC 2617 section 1.2:
 
56
                 * The realm value (case-sensitive), in combination
 
57
                 * with the canonical root URL (the absolute URI for
 
58
                 * the server whose abs_path is empty; see section
 
59
                 * 5.1.2 of [2]) of the server being accessed, defines
 
60
                 * the protection space. */
 
61
                if (entry->realm && !strcmp(entry->realm, realm)) {
 
62
                        /* Exact match. */
 
63
                        break; /* Stop here. */
 
64
                }
 
65
        }
 
66
 
 
67
        return match;
 
68
}
 
69
 
 
70
#define set_auth_user(e, u) \
 
71
        do { \
 
72
                int userlen = int_min((u)->userlen, HTTP_AUTH_USER_MAXLEN - 1); \
 
73
                if (userlen) \
 
74
                        memcpy((e)->user, (u)->user, userlen); \
 
75
                (e)->user[userlen] = 0; \
 
76
        } while (0)
 
77
 
 
78
#define set_auth_password(e, u) \
 
79
        do { \
 
80
                int passwordlen = int_min((u)->passwordlen, HTTP_AUTH_PASSWORD_MAXLEN - 1); \
 
81
                if (passwordlen) \
 
82
                        memcpy((e)->password, (u)->password, passwordlen); \
 
83
                (e)->password[passwordlen] = 0; \
 
84
        } while (0)
 
85
 
 
86
static struct http_auth_basic *
 
87
init_auth_entry(unsigned char *auth_url, unsigned char *realm, struct uri *uri)
 
88
{
 
89
        struct http_auth_basic *entry;
 
90
 
 
91
#ifdef DEBUG_HTTP_AUTH
 
92
        DBG("init_auth_entry: auth_url=%s realm=%s uri=%p", auth_url, realm, uri);
 
93
#endif
 
94
 
 
95
        entry = mem_calloc(1, sizeof(struct http_auth_basic));
 
96
        if (!entry) return NULL;
 
97
 
 
98
        entry->url = auth_url;
 
99
 
 
100
        if (realm) {
 
101
                /* Copy realm value. */
 
102
                entry->realm = stracpy(realm);
 
103
                if (!entry->realm) {
 
104
                        mem_free(entry);
 
105
                        return NULL;
 
106
                }
 
107
        }
 
108
 
 
109
        /* Copy user and pass info passed url if any else NULL terminate. */
 
110
 
 
111
        entry->user = mem_alloc(HTTP_AUTH_USER_MAXLEN + HTTP_AUTH_PASSWORD_MAXLEN);
 
112
        if (!entry->user) {
 
113
                if (entry->realm) mem_free(entry->realm);
 
114
                mem_free(entry);
 
115
                return NULL;
 
116
        }
 
117
        entry->password = entry->user + HTTP_AUTH_USER_MAXLEN;
 
118
        set_auth_user(entry, uri);
 
119
        set_auth_password(entry, uri);
 
120
 
 
121
        return entry;
 
122
}
 
123
 
 
124
/* Add a Basic Auth entry if needed. */
 
125
/* Returns the new entry or updates an existing one. Sets the @valid member if
 
126
 * updating is required so it can be tested if the user should be queried. */
 
127
struct http_auth_basic *
 
128
add_auth_entry(struct uri *uri, unsigned char *realm)
 
129
{
 
130
        struct http_auth_basic *entry;
 
131
        unsigned char *newurl = get_uri_string(uri, ~(URI_DATA | URI_POST));
 
132
 
 
133
#ifdef DEBUG_HTTP_AUTH
 
134
        DBG("add_auth_entry: newurl=%s realm=%s uri=%p", newurl, realm, uri);
 
135
#endif
 
136
 
 
137
        if (!newurl) return NULL;
 
138
 
 
139
        /* Is host/realm already known ? */
 
140
        entry = find_auth_entry(newurl, realm);
 
141
        if (entry) {
 
142
                mem_free(newurl);
 
143
 
 
144
                if (entry->blocked == 1) {
 
145
                        /* Waiting for user/pass in dialog. */
 
146
                        return NULL;
 
147
                }
 
148
 
 
149
                /* In order to use an existing entry it has to match exactly.
 
150
                 * This is done step by step. If something isn't equal the
 
151
                 * entry is updated and marked as invalid. */
 
152
 
 
153
                /* If only one realm is defined or they don't compare. */
 
154
                if ((!!realm ^ !!entry->realm)
 
155
                    || (realm && entry->realm && strcmp(realm, entry->realm))) {
 
156
                        entry->valid = 0;
 
157
                        if (entry->realm) {
 
158
                                mem_free(entry->realm);
 
159
                                entry->realm = NULL;
 
160
                        }
 
161
                        if (realm) {
 
162
                                entry->realm = stracpy(realm);
 
163
                                if (!entry->realm) {
 
164
                                        del_auth_entry(entry);
 
165
                                        return NULL;
 
166
                                }
 
167
                        }
 
168
                }
 
169
 
 
170
                if (!*entry->user
 
171
                    || (!uri->user || !uri->userlen ||
 
172
                        strlcmp(entry->user, -1, uri->user, uri->userlen))) {
 
173
                        entry->valid = 0;
 
174
                        set_auth_user(entry, uri);
 
175
                }
 
176
 
 
177
                if (!*entry->password
 
178
                    || (!uri->password || !uri->passwordlen ||
 
179
                        strlcmp(entry->password, -1, uri->password, uri->passwordlen))) {
 
180
                        entry->valid = 0;
 
181
                        set_auth_password(entry, uri);
 
182
                }
 
183
 
 
184
        } else {
 
185
                /* Create a new entry. */
 
186
                entry = init_auth_entry(newurl, realm, uri);
 
187
                if (!entry) {
 
188
                        mem_free(newurl);
 
189
                        return NULL;
 
190
                }
 
191
 
 
192
                add_to_list(http_auth_basic_list, entry);
 
193
        }
 
194
 
 
195
        return entry;
 
196
}
 
197
 
 
198
/* Find an entry in auth list by url. If url contains user/pass information
 
199
 * and entry does not exist then entry is created.
 
200
 * If entry exists but user/pass passed in url is different, then entry is
 
201
 * updated (but not if user/pass is set in dialog). */
 
202
/* It returns a base 64 encoded user + pass suitable to use in Authorization
 
203
 * header, or NULL on failure. */
 
204
unsigned char *
 
205
find_auth(struct uri *uri)
 
206
{
 
207
        struct http_auth_basic *entry = NULL;
 
208
        unsigned char *id, *ret;
 
209
        unsigned char *newurl = get_uri_string(uri, ~(URI_DATA | URI_POST));
 
210
 
 
211
#ifdef DEBUG_HTTP_AUTH
 
212
        DBG("find_auth: newurl=%s uri=%p", newurl, uri);
 
213
#endif
 
214
 
 
215
        if (!newurl) return NULL;
 
216
 
 
217
        entry = find_auth_entry(newurl, NULL);
 
218
        mem_free(newurl);
 
219
 
 
220
        /* Check is user/pass info is in url. */
 
221
        if (uri->userlen || uri->passwordlen) {
 
222
                /* If there's no entry a new one is added else if the entry
 
223
                 * does not correspond to any existing one update it with the
 
224
                 * user and password from the uri. */
 
225
                if (!entry
 
226
                    || (auth_entry_has_userinfo(entry)
 
227
                        && !strlcmp(entry->password, -1, uri->password, uri->passwordlen)
 
228
                        && !strlcmp(entry->user, -1, uri->user, uri->userlen))) {
 
229
 
 
230
                        entry = add_auth_entry(uri, NULL);
 
231
                }
 
232
        }
 
233
 
 
234
        /* No entry found. */
 
235
        if (!entry) return NULL;
 
236
 
 
237
        /* Sanity check. */
 
238
        if (!auth_entry_has_userinfo(entry)) {
 
239
                del_auth_entry(entry);
 
240
                return NULL;
 
241
        }
 
242
 
 
243
        /* RFC2617 section 2 [Basic Authentication Scheme]
 
244
         * To receive authorization, the client sends the userid and password,
 
245
         * separated by a single colon (":") character, within a base64 [7]
 
246
         * encoded string in the credentials. */
 
247
 
 
248
        /* Create base64 encoded string. */
 
249
        id = straconcat(entry->user, ":", entry->password, NULL);
 
250
        if (!id) return NULL;
 
251
        ret = base64_encode(id);
 
252
        mem_free(id);
 
253
 
 
254
        return ret;
 
255
}
 
256
 
 
257
/* Delete an entry from auth list. */
 
258
void
 
259
del_auth_entry(struct http_auth_basic *entry)
 
260
{
 
261
#ifdef DEBUG_HTTP_AUTH
 
262
        DBG("del_auth_entry: url=%s realm=%s user=%p",
 
263
              entry->url, entry->realm, entry->user);
 
264
#endif
 
265
 
 
266
        if (entry->url) mem_free(entry->url);
 
267
        if (entry->realm) mem_free(entry->realm);
 
268
        if (entry->user) mem_free(entry->user);
 
269
        /* if (entry->password) mem_free(entry->user); Allocated at the same
 
270
         * time as user field, so no need to free it. */
 
271
 
 
272
        del_from_list(entry);
 
273
        mem_free(entry);
 
274
}
 
275
 
 
276
/* Free all entries in auth list and questions in queue. */
 
277
void
 
278
free_auth(void)
 
279
{
 
280
#ifdef DEBUG_HTTP_AUTH
 
281
        DBG("free_auth");
 
282
#endif
 
283
 
 
284
        while (!list_empty(http_auth_basic_list))
 
285
                del_auth_entry(http_auth_basic_list.next);
 
286
 
 
287
        free_list(questions_queue);
 
288
}
 
289
 
 
290
struct http_auth_basic *
 
291
get_invalid_auth_entry(void)
 
292
{
 
293
        struct http_auth_basic *entry;
 
294
 
 
295
#ifdef DEBUG_HTTP_AUTH
 
296
        DBG("get_invalid_auth_entry");
 
297
#endif
 
298
 
 
299
        foreach (entry, http_auth_basic_list)
 
300
                if (!entry->valid)
 
301
                        return entry;
 
302
 
 
303
        return NULL;
 
304
}