1 /* HTTP Authentication support */
11 #include "bfu/hierbox.h"
12 #include "intl/gettext/libintl.h"
13 #include "main/module.h"
14 #include "protocol/auth/auth.h"
15 #include "protocol/auth/dialogs.h"
16 #include "protocol/protocol.h"
17 #include "protocol/uri.h"
18 #include "session/session.h"
19 #include "util/base64.h"
20 #include "util/error.h"
21 #include "util/memory.h"
22 #include "util/string.h"
24 /* Defines to 1 to enable http auth debugging output. */
26 #define DEBUG_HTTP_AUTH
29 static INIT_LIST_OF(struct auth_entry
, auth_entry_list
);
32 /* Find if url/realm is in auth list. If a matching url is found, but realm is
33 * NULL, it returns the first record found. If realm isn't NULL, it returns
34 * the first record that matches exactly (url and realm) if any. */
35 static struct auth_entry
*
36 find_auth_entry(struct uri
*uri
, unsigned char *realm
)
38 struct auth_entry
*match
= NULL
, *entry
;
40 #ifdef DEBUG_HTTP_AUTH
41 DBG("find_auth_entry: url=%s realm=%s", url
, realm
);
44 foreach (entry
, auth_entry_list
) {
45 if (!compare_uri(entry
->uri
, uri
, URI_HTTP_AUTH
))
48 /* Found a matching url. */
51 /* Since realm is NULL, stops immediatly. */
55 /* From RFC 2617 section 1.2:
57 * The realm value (case-sensitive), in combination with the
58 * canonical root URL (the absolute URI for the server whose
59 * abs_path is empty; see section 5.1.2 of [2]) of the server
60 * being accessed, defines the protection space. */
61 if (entry
->realm
&& !strcmp(entry
->realm
, realm
)) {
63 break; /* Stop here. */
71 set_auth_user(struct auth_entry
*entry
, struct uri
*uri
)
73 int userlen
= int_min(uri
->userlen
, AUTH_USER_MAXLEN
- 1);
76 memcpy(entry
->user
, uri
->user
, userlen
);
78 entry
->user
[userlen
] = '\0';
82 set_auth_password(struct auth_entry
*entry
, struct uri
*uri
)
84 int passwordlen
= int_min(uri
->passwordlen
, AUTH_PASSWORD_MAXLEN
- 1);
87 memcpy(entry
->password
, uri
->password
, passwordlen
);
89 entry
->password
[passwordlen
] = '\0';
92 static void done_auth_entry(struct auth_entry
*entry
);
94 static struct auth_entry
*
95 init_auth_entry(struct uri
*uri
, unsigned char *realm
)
97 struct auth_entry
*entry
;
99 #ifdef DEBUG_HTTP_AUTH
100 DBG("init_auth_entry: auth_url=%s realm=%s uri=%p", auth_url
, realm
, uri
);
103 entry
= mem_calloc(1, sizeof(*entry
));
104 if (!entry
) return NULL
;
106 entry
->uri
= get_uri_reference(uri
);
109 /* Copy realm value. */
110 entry
->realm
= stracpy(realm
);
117 /* Copy user and pass info passed url if any else NULL terminate. */
119 set_auth_user(entry
, uri
);
120 set_auth_password(entry
, uri
);
122 entry
->box_item
= add_listbox_leaf(&auth_browser
, NULL
, entry
);
123 if (!entry
->box_item
) {
124 done_auth_entry(entry
);
131 /* Add a Basic Auth entry if needed. */
132 /* Returns the new entry or updates an existing one. Sets the @valid member if
133 * updating is required so it can be tested if the user should be queried. */
135 add_auth_entry(struct uri
*uri
, unsigned char *realm
, unsigned char *nonce
,
136 unsigned char *opaque
, unsigned int digest
)
138 struct auth_entry
*entry
;
140 #ifdef DEBUG_HTTP_AUTH
141 DBG("add_auth_entry: newurl=%s realm=%s uri=%p", newurl
, realm
, uri
);
144 /* Is host/realm already known ? */
145 entry
= find_auth_entry(uri
, realm
);
147 /* Waiting for user/pass in dialog. */
148 if (entry
->blocked
) return NULL
;
150 /* In order to use an existing entry it has to match exactly.
151 * This is done step by step. If something isn't equal the
152 * entry is updated and marked as invalid. */
154 /* If only one realm is defined or they don't compare. */
155 if ((!!realm
^ !!entry
->realm
)
156 || (realm
&& entry
->realm
&& strcmp(realm
, entry
->realm
))) {
158 mem_free_set(&entry
->realm
, NULL
);
160 entry
->realm
= stracpy(realm
);
162 del_auth_entry(entry
);
166 mem_free_set(&entry
->nonce
, stracpy(nonce
));
168 del_auth_entry(entry
);
173 mem_free_set(&entry
->opaque
, stracpy(opaque
));
174 if (!entry
->opaque
) {
175 del_auth_entry(entry
);
179 entry
->digest
= digest
;
184 || (!uri
->user
|| !uri
->userlen
||
185 strlcmp(entry
->user
, -1, uri
->user
, uri
->userlen
))) {
187 set_auth_user(entry
, uri
);
190 if (!*entry
->password
191 || (!uri
->password
|| !uri
->passwordlen
||
192 strlcmp(entry
->password
, -1, uri
->password
, uri
->passwordlen
))) {
194 set_auth_password(entry
, uri
);
198 /* Create a new entry. */
199 entry
= init_auth_entry(uri
, realm
);
200 if (!entry
) return NULL
;
201 add_to_list(auth_entry_list
, entry
);
203 entry
->nonce
= stracpy(nonce
);
205 del_auth_entry(entry
);
210 entry
->opaque
= stracpy(opaque
);
211 if (!entry
->opaque
) {
212 del_auth_entry(entry
);
216 entry
->digest
= digest
;
219 /* Only pop up question if one of the protocols requested it */
220 if (entry
&& !entry
->valid
&& entry
->realm
)
221 add_questions_entry(do_auth_dialog
, entry
);
226 /* Find an entry in auth list by url. If url contains user/pass information
227 * and entry does not exist then entry is created.
228 * If entry exists but user/pass passed in url is different, then entry is
229 * updated (but not if user/pass is set in dialog). */
230 /* It returns a base 64 encoded user + pass suitable to use in Authorization
231 * header, or NULL on failure. */
233 find_auth(struct uri
*uri
)
235 struct auth_entry
*entry
= NULL
;
237 #ifdef DEBUG_HTTP_AUTH
238 DBG("find_auth: newurl=%s uri=%p", newurl
, uri
);
241 entry
= find_auth_entry(uri
, NULL
);
242 /* Check is user/pass info is in url. */
243 if (uri
->userlen
|| uri
->passwordlen
) {
244 /* Add a new entry either to save the user/password info from the URI
245 * so it is available if we later get redirected to a URI with
246 * the user/password stripped. Else if update with entry with
247 * the user/password from the URI. */
249 || (uri
->userlen
&& strlcmp(entry
->user
, -1, uri
->user
, uri
->userlen
))
250 || (uri
->password
&& strlcmp(entry
->password
, -1, uri
->password
, uri
->passwordlen
))) {
252 entry
= add_auth_entry(uri
, NULL
, NULL
, NULL
, 0);
256 /* No entry found or waiting for user/password in dialog. */
257 if (!entry
|| entry
->blocked
)
261 if (!auth_entry_has_userinfo(entry
)) {
262 del_auth_entry(entry
);
270 done_auth_entry(struct auth_entry
*entry
)
273 done_listbox_item(&auth_browser
, entry
->box_item
);
274 done_uri(entry
->uri
);
275 mem_free_if(entry
->realm
);
276 mem_free_if(entry
->nonce
);
277 mem_free_if(entry
->opaque
);
281 /* Delete an entry from auth list. */
283 del_auth_entry(struct auth_entry
*entry
)
285 #ifdef DEBUG_HTTP_AUTH
286 DBG("del_auth_entry: url=%s realm=%s user=%p",
287 entry
->url
, entry
->realm
, entry
->user
);
290 del_from_list(entry
);
292 done_auth_entry(entry
);
295 /* Free all entries in auth list and questions in queue. */
299 #ifdef DEBUG_HTTP_AUTH
303 while (!list_empty(auth_entry_list
))
304 del_auth_entry(auth_entry_list
.next
);
306 free_list(questions_queue
);
310 done_auth(struct module
*xxx
)
316 get_invalid_auth_entry(void)
318 struct auth_entry
*entry
;
320 #ifdef DEBUG_HTTP_AUTH
321 DBG("get_invalid_auth_entry");
324 foreach (entry
, auth_entry_list
)
331 struct module auth_module
= struct_module(
332 /* name: */ N_("Authentication"),
335 /* submodules: */ NULL
,
338 /* done: */ done_auth