Add get_terminal_codepage().
[elinks.git] / src / protocol / auth / auth.c
blobdc1c1643ff534a92a0f1ffb606af366f8f74cdac
1 /* HTTP Authentication support */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <string.h>
9 #include "elinks.h"
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. */
25 #if 0
26 #define DEBUG_HTTP_AUTH
27 #endif
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);
42 #endif
44 foreach (entry, auth_entry_list) {
45 if (!compare_uri(entry->uri, uri, URI_HTTP_AUTH))
46 continue;
48 /* Found a matching url. */
49 match = entry;
50 if (!realm) {
51 /* Since realm is NULL, stops immediatly. */
52 break;
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)) {
62 /* Exact match. */
63 break; /* Stop here. */
67 return match;
70 static void
71 set_auth_user(struct auth_entry *entry, struct uri *uri)
73 int userlen = int_min(uri->userlen, AUTH_USER_MAXLEN - 1);
75 if (userlen)
76 memcpy(entry->user, uri->user, userlen);
78 entry->user[userlen] = '\0';
81 static void
82 set_auth_password(struct auth_entry *entry, struct uri *uri)
84 int passwordlen = int_min(uri->passwordlen, AUTH_PASSWORD_MAXLEN - 1);
86 if (passwordlen)
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);
101 #endif
103 entry = mem_calloc(1, sizeof(*entry));
104 if (!entry) return NULL;
106 entry->uri = get_uri_reference(uri);
108 if (realm) {
109 /* Copy realm value. */
110 entry->realm = stracpy(realm);
111 if (!entry->realm) {
112 mem_free(entry);
113 return NULL;
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);
125 return NULL;
128 return 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. */
134 struct auth_entry *
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);
142 #endif
144 /* Is host/realm already known ? */
145 entry = find_auth_entry(uri, realm);
146 if (entry) {
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))) {
157 entry->valid = 0;
158 mem_free_set(&entry->realm, NULL);
159 if (realm) {
160 entry->realm = stracpy(realm);
161 if (!entry->realm) {
162 del_auth_entry(entry);
163 return NULL;
165 if (nonce) {
166 mem_free_set(&entry->nonce, stracpy(nonce));
167 if (!entry->nonce) {
168 del_auth_entry(entry);
169 return NULL;
172 if (opaque) {
173 mem_free_set(&entry->opaque, stracpy(opaque));
174 if (!entry->opaque) {
175 del_auth_entry(entry);
176 return NULL;
179 entry->digest = digest;
183 if (!*entry->user
184 || (!uri->user || !uri->userlen ||
185 strlcmp(entry->user, -1, uri->user, uri->userlen))) {
186 entry->valid = 0;
187 set_auth_user(entry, uri);
190 if (!*entry->password
191 || (!uri->password || !uri->passwordlen ||
192 strlcmp(entry->password, -1, uri->password, uri->passwordlen))) {
193 entry->valid = 0;
194 set_auth_password(entry, uri);
197 } else {
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);
202 if (nonce) {
203 entry->nonce = stracpy(nonce);
204 if (!entry->nonce) {
205 del_auth_entry(entry);
206 return NULL;
209 if (opaque) {
210 entry->opaque = stracpy(opaque);
211 if (!entry->opaque) {
212 del_auth_entry(entry);
213 return NULL;
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);
223 return 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. */
232 struct auth_entry *
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);
239 #endif
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. */
248 if (!entry
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)
258 return NULL;
260 /* Sanity check. */
261 if (!auth_entry_has_userinfo(entry)) {
262 del_auth_entry(entry);
263 return NULL;
266 return entry;
269 static void
270 done_auth_entry(struct auth_entry *entry)
272 if (entry->box_item)
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);
278 mem_free(entry);
281 /* Delete an entry from auth list. */
282 void
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);
288 #endif
290 del_from_list(entry);
292 done_auth_entry(entry);
295 /* Free all entries in auth list and questions in queue. */
296 void
297 free_auth(void)
299 #ifdef DEBUG_HTTP_AUTH
300 DBG("free_auth");
301 #endif
303 while (!list_empty(auth_entry_list))
304 del_auth_entry(auth_entry_list.next);
306 free_list(questions_queue);
309 static void
310 done_auth(struct module *xxx)
312 free_auth();
315 struct auth_entry *
316 get_invalid_auth_entry(void)
318 struct auth_entry *entry;
320 #ifdef DEBUG_HTTP_AUTH
321 DBG("get_invalid_auth_entry");
322 #endif
324 foreach (entry, auth_entry_list)
325 if (!entry->valid)
326 return entry;
328 return NULL;
331 struct module auth_module = struct_module(
332 /* name: */ N_("Authentication"),
333 /* options: */ NULL,
334 /* hooks: */ NULL,
335 /* submodules: */ NULL,
336 /* data: */ NULL,
337 /* init: */ NULL,
338 /* done: */ done_auth