1
/* HTTP Authentication support */
2
/* $Id: auth.c,v 1.70 2003/12/21 14:51:20 zas Exp $ */
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"
23
/* Defines to 1 to enable http auth debugging output. */
25
#define DEBUG_HTTP_AUTH
28
static INIT_LIST_HEAD(http_auth_basic_list);
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)
37
struct http_auth_basic *match = NULL, *entry;
39
#ifdef DEBUG_HTTP_AUTH
40
DBG("find_auth_entry: url=%s realm=%s", url, realm);
43
if (!url || !*url) return NULL;
45
foreach (entry, http_auth_basic_list) {
46
if (strcasecmp(entry->url, url)) continue;
48
/* Found a matching url. */
51
/* Since realm is NULL, stops immediatly. */
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)) {
63
break; /* Stop here. */
70
#define set_auth_user(e, u) \
72
int userlen = int_min((u)->userlen, HTTP_AUTH_USER_MAXLEN - 1); \
74
memcpy((e)->user, (u)->user, userlen); \
75
(e)->user[userlen] = 0; \
78
#define set_auth_password(e, u) \
80
int passwordlen = int_min((u)->passwordlen, HTTP_AUTH_PASSWORD_MAXLEN - 1); \
82
memcpy((e)->password, (u)->password, passwordlen); \
83
(e)->password[passwordlen] = 0; \
86
static struct http_auth_basic *
87
init_auth_entry(unsigned char *auth_url, unsigned char *realm, struct uri *uri)
89
struct http_auth_basic *entry;
91
#ifdef DEBUG_HTTP_AUTH
92
DBG("init_auth_entry: auth_url=%s realm=%s uri=%p", auth_url, realm, uri);
95
entry = mem_calloc(1, sizeof(struct http_auth_basic));
96
if (!entry) return NULL;
98
entry->url = auth_url;
101
/* Copy realm value. */
102
entry->realm = stracpy(realm);
109
/* Copy user and pass info passed url if any else NULL terminate. */
111
entry->user = mem_alloc(HTTP_AUTH_USER_MAXLEN + HTTP_AUTH_PASSWORD_MAXLEN);
113
if (entry->realm) mem_free(entry->realm);
117
entry->password = entry->user + HTTP_AUTH_USER_MAXLEN;
118
set_auth_user(entry, uri);
119
set_auth_password(entry, uri);
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)
130
struct http_auth_basic *entry;
131
unsigned char *newurl = get_uri_string(uri, ~(URI_DATA | URI_POST));
133
#ifdef DEBUG_HTTP_AUTH
134
DBG("add_auth_entry: newurl=%s realm=%s uri=%p", newurl, realm, uri);
137
if (!newurl) return NULL;
139
/* Is host/realm already known ? */
140
entry = find_auth_entry(newurl, realm);
144
if (entry->blocked == 1) {
145
/* Waiting for user/pass in dialog. */
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. */
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))) {
158
mem_free(entry->realm);
162
entry->realm = stracpy(realm);
164
del_auth_entry(entry);
171
|| (!uri->user || !uri->userlen ||
172
strlcmp(entry->user, -1, uri->user, uri->userlen))) {
174
set_auth_user(entry, uri);
177
if (!*entry->password
178
|| (!uri->password || !uri->passwordlen ||
179
strlcmp(entry->password, -1, uri->password, uri->passwordlen))) {
181
set_auth_password(entry, uri);
185
/* Create a new entry. */
186
entry = init_auth_entry(newurl, realm, uri);
192
add_to_list(http_auth_basic_list, entry);
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. */
205
find_auth(struct uri *uri)
207
struct http_auth_basic *entry = NULL;
208
unsigned char *id, *ret;
209
unsigned char *newurl = get_uri_string(uri, ~(URI_DATA | URI_POST));
211
#ifdef DEBUG_HTTP_AUTH
212
DBG("find_auth: newurl=%s uri=%p", newurl, uri);
215
if (!newurl) return NULL;
217
entry = find_auth_entry(newurl, NULL);
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. */
226
|| (auth_entry_has_userinfo(entry)
227
&& !strlcmp(entry->password, -1, uri->password, uri->passwordlen)
228
&& !strlcmp(entry->user, -1, uri->user, uri->userlen))) {
230
entry = add_auth_entry(uri, NULL);
234
/* No entry found. */
235
if (!entry) return NULL;
238
if (!auth_entry_has_userinfo(entry)) {
239
del_auth_entry(entry);
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. */
248
/* Create base64 encoded string. */
249
id = straconcat(entry->user, ":", entry->password, NULL);
250
if (!id) return NULL;
251
ret = base64_encode(id);
257
/* Delete an entry from auth list. */
259
del_auth_entry(struct http_auth_basic *entry)
261
#ifdef DEBUG_HTTP_AUTH
262
DBG("del_auth_entry: url=%s realm=%s user=%p",
263
entry->url, entry->realm, entry->user);
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. */
272
del_from_list(entry);
276
/* Free all entries in auth list and questions in queue. */
280
#ifdef DEBUG_HTTP_AUTH
284
while (!list_empty(http_auth_basic_list))
285
del_auth_entry(http_auth_basic_list.next);
287
free_list(questions_queue);
290
struct http_auth_basic *
291
get_invalid_auth_entry(void)
293
struct http_auth_basic *entry;
295
#ifdef DEBUG_HTTP_AUTH
296
DBG("get_invalid_auth_entry");
299
foreach (entry, http_auth_basic_list)