summaryrefslogtreecommitdiff
path: root/content
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2006-02-19 18:26:23 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2006-02-19 18:26:23 +0000
commit5ce5fe084c733c95544825e35bcd63cc775aee94 (patch)
tree23a1fae1a862e20c891c576325731963dbeaef39 /content
parent7dbc14cf05a7417f372fdda22f20622f8f72175f (diff)
downloadnetsurf-5ce5fe084c733c95544825e35bcd63cc775aee94.tar.gz
netsurf-5ce5fe084c733c95544825e35bcd63cc775aee94.tar.bz2
[project @ 2006-02-19 18:26:23 by jmb]
Rewrite HTTP authentication. Fix extraction of realm from WWW-Authenticate header. Tidy up login dialog code. svn path=/import/netsurf/; revision=2085
Diffstat (limited to 'content')
-rw-r--r--content/authdb.c366
-rw-r--r--content/authdb.h18
-rw-r--r--content/fetch.c23
3 files changed, 400 insertions, 7 deletions
diff --git a/content/authdb.c b/content/authdb.c
new file mode 100644
index 000000000..f97adb1b0
--- /dev/null
+++ b/content/authdb.c
@@ -0,0 +1,366 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2006 John M Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+/** \file
+ * HTTP authentication database (implementation)
+ *
+ * Authentication details are stored hashed by canonical root URI
+ * (absoluteURI with no abs_path part - see RFC 2617) for fast lookup.
+ *
+ * A protection space is specified by the root URI and a case sensitive
+ * realm match. User-agents may preemptively send authentication details
+ * for locations within a currently known protected space (i.e:
+ * Given a known realm URI of scheme://authority/path/to/realm/
+ * the URI scheme://authority/path/to/realm/foo/ can be assumed to
+ * be within the protection space.)
+ *
+ * In order to deal with realms within realms, the realm details are stored
+ * such that the most specific URI comes first (where "most specific" is
+ * classed as the one with the longest abs_path segment).
+ *
+ * Realms spanning domains are stored multiple times (once per domain).
+ *
+ * Where a higher level resource is found to be within a known realm, the
+ * existing match is replaced with the new one (i.e:
+ * Given a known realm of scheme://authority/path/to/realm/ (uri1)
+ * and the newly-acquired knowledge that scheme://authority/path/to/ (uri2)
+ * lies within the same realm, the realm details for uri1 are replaced with
+ * those for uri2. - in most cases, this is likely to be a simple
+ * replacement of the realm URI)
+ *
+ * There is currently no mechanism for retaining authentication details over
+ * sessions.
+ */
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "netsurf/content/authdb.h"
+#define NDEBUG
+#include "netsurf/utils/log.h"
+#include "netsurf/utils/url.h"
+
+#define HASH_SIZE 77
+
+struct realm_details {
+ char *realm; /**< Realm identifier */
+ char *url; /**< Base URL of realm */
+ char *auth; /**< Authentication details */
+ struct realm_details *next;
+ struct realm_details *prev;
+};
+
+struct auth_entry {
+ char *root_url; /**< Canonical root URL of realms */
+ struct realm_details *realms; /**< List of realms on this host */
+ struct auth_entry *next;
+};
+
+static struct auth_entry *auth_table[HASH_SIZE];
+
+static unsigned int authdb_hash(const char *s);
+static struct realm_details *authdb_get_rd(const char *canon,
+ const char *url, const char *realm);
+static void authdb_dump(void);
+
+/**
+ * Insert an entry into the database, potentially replacing any
+ * existing entry.
+ *
+ * \param url Absolute URL to resource
+ * \param realm Authentication realm containing resource
+ * \param auth Authentication details in form "username:password"
+ * \return true on success, false on error.
+ */
+bool authdb_insert(const char *url, const char *realm, const char *auth)
+{
+ char *canon, *stripped;
+ unsigned int hash;
+ struct realm_details *rd;
+ struct auth_entry *entry;
+ url_func_result ret;
+
+ assert(url && realm && auth);
+
+ LOG(("Adding '%s' - '%s'", url, realm));
+
+ ret = url_canonical_root(url, &canon);
+ if (ret != URL_FUNC_OK)
+ return false;
+
+ LOG(("'%s'", canon));
+
+ ret = url_strip_lqf(url, &stripped);
+ if (ret != URL_FUNC_OK) {
+ free(canon);
+ return false;
+ }
+
+ hash = authdb_hash(canon);
+
+ /* Look for existing entry */
+ for (entry = auth_table[hash]; entry; entry = entry->next)
+ if (strcmp(entry->root_url, canon) == 0)
+ break;
+
+ rd = authdb_get_rd(canon, stripped, realm);
+ if (rd) {
+ /* We have a match */
+ if (strlen(stripped) < strlen(rd->url)) {
+ /* more generic, so update URL and move to
+ * appropriate location in list (s.t. the invariant
+ * that most specific URLs come first is maintained)
+ */
+ struct realm_details *r, *s;
+ char *temp = strdup(auth);
+
+ if (!temp) {
+ free(temp);
+ free(stripped);
+ free(canon);
+ return false;
+ }
+
+ free(rd->url);
+ rd->url = stripped;
+
+ free(rd->auth);
+ rd->auth = temp;
+
+ for (r = rd->next; r; r = s) {
+ s = r->next;
+ if (strlen(r->url) > strlen(rd->url)) {
+ rd->next->prev = rd->prev;
+ if (rd->prev)
+ rd->prev->next = rd->next;
+ else
+ entry->realms = r;
+
+ rd->prev = r;
+ rd->next = r->next;
+ if (r->next)
+ r->next->prev = rd;
+ r->next = rd;
+ }
+ }
+ }
+ else if (strlen(stripped) == strlen(rd->url)) {
+ /* exact match, so replace auth details */
+ char *temp = strdup(auth);
+ if (!temp) {
+ free(stripped);
+ free(canon);
+ return false;
+ }
+
+ free(rd->auth);
+ rd->auth = temp;
+
+ free(stripped);
+ }
+ /* otherwise, nothing to do */
+
+ free(canon);
+ return true;
+ }
+
+ /* no existing entry => create one */
+ rd = malloc(sizeof(struct realm_details));
+ if (!rd) {
+ free(stripped);
+ free(canon);
+ return false;
+ }
+
+ rd->realm = strdup(realm);
+ rd->auth = strdup(auth);
+ rd->url = stripped;
+ rd->prev = 0;
+
+ if (!rd->realm || !rd->auth || ret != URL_FUNC_OK) {
+ free(rd->url);
+ free(rd->auth);
+ free(rd->realm);
+ free(rd);
+ free(canon);
+ return false;
+ }
+
+ if (entry) {
+ /* found => add to it */
+ rd->next = entry->realms;
+ if (entry->realms)
+ entry->realms->prev = rd;
+ entry->realms = rd;
+
+ free(canon);
+ return true;
+ }
+
+ /* not found => create new */
+ entry = malloc(sizeof(struct auth_entry));
+ if (!entry) {
+ free(rd->url);
+ free(rd->auth);
+ free(rd->realm);
+ free(rd);
+ free(canon);
+ return false;
+ }
+
+ rd->next = 0;
+ entry->root_url = canon;
+ entry->realms = rd;
+ entry->next = auth_table[hash];
+ auth_table[hash] = entry;
+
+ return true;
+}
+
+/**
+ * Find realm details entry
+ *
+ * \param canon Canonical root URL
+ * \param url Stripped URL to resource
+ * \param realm Realm containing resource
+ * \return Realm details or NULL if not found
+ */
+struct realm_details *authdb_get_rd(const char *canon, const char *url,
+ const char *realm)
+{
+ struct auth_entry *entry;
+ struct realm_details *ret;
+
+ assert(canon && url);
+
+ for (entry = auth_table[authdb_hash(canon)]; entry;
+ entry = entry->next)
+ if (strcmp(entry->root_url, canon) == 0)
+ break;
+
+ if (!entry)
+ return NULL;
+
+ for (ret = entry->realms; ret; ret = ret->next) {
+ if (strcmp(ret->realm, realm))
+ /* skip realms that don't match */
+ continue;
+ if (strlen(url) >= strlen(ret->url) &&
+ !strncmp(url, ret->url, strlen(ret->url)))
+ /* If the requested URL is of equal or greater
+ * specificity than the stored one, but is within
+ * the same realm, then use the more generic details
+ */
+ return ret;
+ else if (strncmp(url, ret->url, strlen(url)) == 0) {
+ /* We have a more general URL in the same realm */
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Retrieve authentication details for an URL from the database
+ *
+ * \param url Absolute URL to consider
+ * \return authentication details, or NULL if none found.
+ */
+const char *authdb_get(const char *url)
+{
+ char *canon, *stripped;
+ struct auth_entry *entry;
+ struct realm_details *rd;
+ url_func_result ret;
+
+ assert(url);
+
+ LOG(("Searching for '%s'", url));
+
+ authdb_dump();
+
+ ret = url_canonical_root(url, &canon);
+ if (ret != URL_FUNC_OK)
+ return NULL;
+
+ ret = url_strip_lqf(url, &stripped);
+ if (ret != URL_FUNC_OK) {
+ free(canon);
+ return NULL;
+ }
+
+ /* Find auth entry */
+ for (entry = auth_table[authdb_hash(canon)]; entry;
+ entry = entry->next)
+ if (strcmp(entry->root_url, canon) == 0)
+ break;
+
+ if (!entry) {
+ free(stripped);
+ free(canon);
+ return NULL;
+ }
+
+ LOG(("Found entry"));
+
+ /* Find realm details */
+ for (rd = entry->realms; rd; rd = rd->next)
+ if (strlen(stripped) >= strlen(rd->url) &&
+ !strncmp(stripped, rd->url, strlen(rd->url)))
+ break;
+
+ if (!rd) {
+ free(stripped);
+ free(canon);
+ return NULL;
+ }
+
+ LOG(("Found realm"));
+
+ free(stripped);
+ free(canon);
+ return rd->auth;
+}
+
+/**
+ * Hash function for keys.
+ */
+unsigned int authdb_hash(const char *s)
+{
+ unsigned int i, z = 0, m;
+ if (!s)
+ return 0;
+
+ m = strlen(s);
+
+ for (i = 0; i != m && s[i]; i++)
+ z += s[i] & 0x1f; /* lower 5 bits, case insensitive */
+ return z % HASH_SIZE;
+}
+
+/**
+ * Dump contents of auth db to stderr
+ */
+void authdb_dump(void)
+{
+#ifndef NDEBUG
+ int i;
+ struct auth_entry *e;
+ struct realm_details *r;
+
+ for (i = 0; i != HASH_SIZE; i++) {
+ LOG(("%d:", i));
+ for (e = auth_table[i]; e; e = e->next) {
+ LOG(("\t%s", e->root_url));
+ for (r = e->realms; r; r = r->next) {
+ LOG(("\t\t%s - %s", r->url, r->realm));
+ }
+ }
+ }
+#endif
+}
diff --git a/content/authdb.h b/content/authdb.h
new file mode 100644
index 000000000..ece7b763d
--- /dev/null
+++ b/content/authdb.h
@@ -0,0 +1,18 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2006 John M Bell <jmb202@ecs.soton.ac.uk>
+ */
+
+/** \file
+ * HTTP authentication database (interface)
+ */
+
+#ifndef _NETSURF_CONTENT_AUTHDB_H_
+#define _NETSURF_CONTENT_AUTHDB_H_
+
+bool authdb_insert(const char *url, const char *realm, const char *auth);
+const char *authdb_get(const char *url);
+
+#endif
diff --git a/content/fetch.c b/content/fetch.c
index b63bf5f91..0d9ccd583 100644
--- a/content/fetch.c
+++ b/content/fetch.c
@@ -31,11 +31,11 @@
#endif
#include "curl/curl.h"
#include "netsurf/utils/config.h"
-#include "netsurf/content/fetch.h"
-#include "netsurf/desktop/options.h"
#ifdef WITH_AUTH
-#include "netsurf/desktop/401login.h"
+#include "netsurf/content/authdb.h"
#endif
+#include "netsurf/content/fetch.h"
+#include "netsurf/desktop/options.h"
#include "netsurf/render/form.h"
#define NDEBUG
#include "netsurf/utils/log.h"
@@ -414,7 +414,7 @@ failed:
CURLcode fetch_set_options(struct fetch *f)
{
CURLcode code;
- struct login *li;
+ const char *auth;
#undef SETOPT
#define SETOPT(option, value) \
@@ -445,12 +445,16 @@ CURLcode fetch_set_options(struct fetch *f)
SETOPT(CURLOPT_COOKIEFILE, 0);
SETOPT(CURLOPT_COOKIEJAR, 0);
}
- if ((li = login_list_get(f->url)) != NULL) {
+#ifdef WITH_AUTH
+ if ((auth = authdb_get(f->url)) != NULL) {
SETOPT(CURLOPT_HTTPAUTH, CURLAUTH_ANY);
- SETOPT(CURLOPT_USERPWD, li->logindetails);
+ SETOPT(CURLOPT_USERPWD, auth);
} else {
+#endif
SETOPT(CURLOPT_USERPWD, 0);
+#ifdef WITH_AUTH
}
+#endif
if (option_http_proxy && option_http_proxy_host) {
SETOPT(CURLOPT_PROXY, option_http_proxy_host);
SETOPT(CURLOPT_PROXYPORT, (long) option_http_proxy_port);
@@ -796,8 +800,13 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
return size;
}
SKIP_ST(17);
- while (i < (int)size && data[++i] == '"')
+
+ while (i < (int) size && strncasecmp(data + i, "realm", 5))
+ i++;
+ while (i < (int)size && data[++i] != '"')
/* */;
+ i++;
+
strncpy(f->realm, data + i, size - i);
f->realm[size - i] = '\0';
for (i = size - i - 1; i >= 0 &&