From 2261b616f61e6701b381d6e363e14431f321f843 Mon Sep 17 00:00:00 2001 From: John Tytgat Date: Tue, 4 Aug 2009 23:02:23 +0000 Subject: - content/urldb.c(auth_data): Removed; (prot_space_data): Added, it lives linked in the leaf host_part struct and together with its scheme and port (which defins canonical root url) and realm this defines a protection space. (path_data): Removed auth_data field and replaced by a prot_space_data pointer. (host_part::prot_space): Added linked list of protection space data structs. (urldb_get_auth_details): Given an URL fetch fetches its auth. (urldb_set_auth_details): Creates or updates the contents of a protection space to which given URL belongs. (urldb_destroy_host_tree): Delete protection data space structures using urldb_destroy_prot_space. (urldb_destroy_prot_space): Added. - content/urldb.h(urldb_get_auth_details): Added realm parameter. - content/fetchers/fetch_curl.c(fetch_curl_set_options): Update urldb_get_auth_details call (we don't know realm at this point). - content/fetchcache.c(fetchcache_callback, fetchcache_auth): At FETCH_AUTH, use realm to determine if we really don't know auth data and if so, refetch content. - content/content.h(struct content): Add content::tried_with_auth. - content/content.c(content_create): Initialize content::tried_with_auth. - riscos/401login.c(ro_gui_401login_open): Show known authentication data in dialogue so user can see what was wrong with it and correct it. Solves bug #2830829. svn path=/trunk/netsurf/; revision=9045 --- content/content.c | 1 + content/content.h | 1 + content/fetchcache.c | 98 ++++++++++++++++++++-- content/fetchers/fetch_curl.c | 2 +- content/urldb.c | 183 +++++++++++++++++++++++++++++------------- content/urldb.h | 2 +- riscos/401login.c | 25 +++++- 7 files changed, 241 insertions(+), 71 deletions(-) diff --git a/content/content.c b/content/content.c index 2b4499a29..828423603 100644 --- a/content/content.c +++ b/content/content.c @@ -443,6 +443,7 @@ struct content * content_create(const char *url) c->http_code = 0; c->no_error_pages = false; c->download = false; + c->tried_with_auth = false; c->redirect_count = 0; c->error_count = 0; c->cache_data.req_time = 0; diff --git a/content/content.h b/content/content.h index 1db6122ee..db35b39a2 100644 --- a/content/content.h +++ b/content/content.h @@ -259,6 +259,7 @@ struct content { bool no_error_pages; /**< Used by fetchcache(). */ bool download; /**< Used by fetchcache(). */ + bool tried_with_auth; /**< Used by fetchcache(). */ unsigned int redirect_count; /**< Used by fetchcache(). */ /** Array of first n rendering errors or warnings. */ diff --git a/content/fetchcache.c b/content/fetchcache.c index 71e39e4fa..3c43e1f60 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -1,5 +1,6 @@ /* * Copyright 2005 James Bursa + * Copyright 2009 John-Mark Bell * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -37,6 +38,7 @@ #include "content/content.h" #include "content/fetchcache.h" #include "content/fetch.h" +#include "content/urldb.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/talloc.h" @@ -58,6 +60,7 @@ static void fetchcache_cache_clone(struct content *c, static void fetchcache_notmodified(struct content *c, const void *data); static void fetchcache_redirect(struct content *c, const void *data, unsigned long size); +static void fetchcache_auth(struct content *c, const char *realm); /** @@ -516,14 +519,7 @@ void fetchcache_callback(fetch_msg msg, void *p, const void *data, break; case FETCH_AUTH: - /* data -> string containing the Realm */ - LOG(("FETCH_AUTH, '%s'", (const char *)data)); - c->fetch = 0; - msg_data.auth_realm = data; - content_broadcast(c, CONTENT_MSG_AUTH, msg_data); - /* set the status to ERROR so that the content is - * destroyed in content_clean() */ - c->status = CONTENT_STATUS_ERROR; + fetchcache_auth(c, data); break; case FETCH_CERT_ERR: @@ -1136,6 +1132,92 @@ void fetchcache_redirect(struct content *c, const void *data, free(referer); } +/** + * Authentication callback handler + */ + +void fetchcache_auth(struct content *c, const char *realm) +{ + char *referer; + const char *ref; + const char *auth; + struct content *parent; + bool parent_was_verifiable; + union content_msg_data msg_data; + char *headers = NULL; + + /* Preconditions */ + assert(c && realm); + assert(c->status == CONTENT_STATUS_TYPE_UNKNOWN); + + /* Extract fetch details */ + ref = fetch_get_referer(c->fetch); + parent = fetch_get_parent(c->fetch); + parent_was_verifiable = fetch_get_verifiable(c->fetch); + + /* Clone referer -- original is destroyed in fetch_abort() */ + referer = ref ? strdup(ref) : NULL; + + fetch_abort(c->fetch); + c->fetch = NULL; + + /* Ensure that referer cloning succeeded + * _must_ be after content invalidation */ + if (ref && !referer) { + LOG(("Failed cloning referer")); + + c->status = CONTENT_STATUS_ERROR; + msg_data.error = messages_get("BadRedirect"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + + return; + } + + /* Now, see if we've got some auth details */ + auth = urldb_get_auth_details(c->url, realm); + + if (auth == NULL || c->tried_with_auth) { + /* No authentication details or we tried what we had, so ask + * our client for them. */ + c->tried_with_auth = false; /* Allow rety. */ + + c->status = CONTENT_STATUS_ERROR; + msg_data.auth_realm = realm; + content_broadcast(c, CONTENT_MSG_AUTH, msg_data); + + return; + } + /* Flag we're retry fetching with auth data. Will be used to detect + * wrong auth data so that we can ask our client for better auth. */ + c->tried_with_auth = true; + + /* We have authentication details. Fetch with them. */ + /** \todo all the useful things like headers, POST. */ + c->fetch = fetch_start(c->url, referer, + fetchcache_callback, c, + c->no_error_pages, + NULL, NULL, parent_was_verifiable, + parent, &headers); + if (c->fetch == NULL) { + char error_message[500]; + + LOG(("warning: fetch_start failed")); + snprintf(error_message, sizeof error_message, + messages_get("InvalidURL"), + c->url); + if (c->no_error_pages) { + c->status = CONTENT_STATUS_ERROR; + msg_data.error = error_message; + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + } else { + fetchcache_error_page(c, error_message); + } + } + + /* Clean up */ + free(referer); +} + #ifdef TEST #include diff --git a/content/fetchers/fetch_curl.c b/content/fetchers/fetch_curl.c index 33425a352..8efe0d726 100644 --- a/content/fetchers/fetch_curl.c +++ b/content/fetchers/fetch_curl.c @@ -560,7 +560,7 @@ fetch_curl_set_options(struct curl_fetch_info *f) SETOPT(CURLOPT_COOKIE, NULL); } - if ((auth = urldb_get_auth_details(f->url)) != NULL) { + if ((auth = urldb_get_auth_details(f->url, NULL)) != NULL) { SETOPT(CURLOPT_HTTPAUTH, CURLAUTH_ANY); SETOPT(CURLOPT_USERPWD, auth); } else { diff --git a/content/urldb.c b/content/urldb.c index 497a7ca8b..8089e0d4e 100644 --- a/content/urldb.c +++ b/content/urldb.c @@ -1,5 +1,6 @@ /* * Copyright 2006 John M Bell + * Copyright 2009 John Tytgat * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -132,10 +133,22 @@ struct cookie_internal_data { struct cookie_internal_data *next; /**< Next in list */ }; -struct auth_data { +/* A protection space is defined as a tuple canonical_root_url and realm. + * This structure lives as linked list element in a leaf host_part struct + * so we need additional scheme and port to have a canonical_root_url. */ +struct prot_space_data { + char *scheme; /**< URL scheme of canonical hostname of this + * protection space. */ + unsigned int port; /**< Port number of canonical hostname of this + * protection space. When 0, it means the + * default port for given scheme, i.e. 80 + * (http), 443 (https). */ char *realm; /**< Protection realm */ - char *auth; /**< Authentication details in form + + char *auth; /**< Authentication details for this + * protection space in form * username:password */ + struct prot_space_data *next; /**< Next sibling */ }; struct cache_internal_data { @@ -152,7 +165,9 @@ struct url_internal_data { struct path_data { char *url; /**< Full URL */ char *scheme; /**< URL scheme for data */ - unsigned int port; /**< Port number for data */ + unsigned int port; /**< Port number for data. When 0, it means + * the default port for given scheme, i.e. + * 80 (http), 443 (https). */ char *segment; /**< Path segment for this node */ unsigned int frag_cnt; /**< Number of entries in ::fragment */ char **fragment; /**< Array of fragments */ @@ -161,7 +176,11 @@ struct path_data { struct bitmap *thumb; /**< Thumbnail image of resource */ struct url_internal_data urld; /**< URL data for resource */ struct cache_internal_data cache; /**< Cache data for resource */ - struct auth_data auth; /**< Authentication data for resource */ + const struct prot_space_data *prot_space; /**< Protection space + * to which this resource belongs too. Can be + * NULL when it does not belong to a protection + * space or when it is not known. No + * ownership (is with struct host_part::prot_space). */ struct cookie_internal_data *cookies; /**< Cookies associated with resource */ struct cookie_internal_data *cookies_end; /**< Last cookie in list */ @@ -183,6 +202,10 @@ struct host_part { char *part; /**< Part of host string */ + struct prot_space_data *prot_space; /**< Linked list of all known + * proctection spaces known for his host and + * all its schems and ports. */ + struct host_part *next; /**< Next sibling */ struct host_part *prev; /**< Previous sibling */ struct host_part *parent; /**< Parent host part */ @@ -203,6 +226,7 @@ static void urldb_destroy_host_tree(struct host_part *root); static void urldb_destroy_path_tree(struct path_data *root); static void urldb_destroy_path_node_content(struct path_data *node); static void urldb_destroy_cookie(struct cookie_internal_data *c); +static void urldb_destroy_prot_space(struct prot_space_data *space); static void urldb_destroy_search_tree(struct search_node *root); /* Saving */ @@ -925,11 +949,13 @@ const char *urldb_get_url(const char *url) * Look up authentication details in database * * \param url Absolute URL to search for + * \param realm When non-NULL, it is realm which can be used to determine + * the protection space when that's not been done before for given URL. * \return Pointer to authentication details, or NULL if not found */ -const char *urldb_get_auth_details(const char *url) +const char *urldb_get_auth_details(const char *url, const char *realm) { - struct path_data *p, *q = NULL; + struct path_data *p, *p_cur, *p_top; assert(url); @@ -940,29 +966,33 @@ const char *urldb_get_auth_details(const char *url) if (!p) return NULL; - /* Check for any auth details attached to this node */ - if (p && p->auth.realm && p->auth.auth) - return p->auth.auth; - - /* Now consider ancestors */ - for (; p; p = p->parent) { - /* The parent path entry is stored hung off the - * parent entry with an empty (not NULL) segment string. - * We look for this here. - */ - for (q = p->children; q; q = q->next) { - if (q->segment[0] == '\0') - break; + /* Check for any auth details attached to the path_data node or any of + * its parents. */ + for (p_cur = p; p_cur != NULL; p_top = p_cur, p_cur = p_cur->parent) { + if (p_cur->prot_space) { + return p_cur->prot_space->auth; } - - if (q && q->auth.realm && q->auth.auth) - break; } - if (!q) - return NULL; + /* Only when we have a realm (and canonical root of given URL), we can + * uniquely locate the protection space. */ + if (realm != NULL) { + const struct host_part *h = (const struct host_part *)p_top; + const struct prot_space_data *space; + + /* Search for a possible matching protection space. */ + for (space = h->prot_space; space != NULL; + space = space->next) { + if (!strcmp(space->realm, realm) + && !strcmp(space->scheme, p->scheme) + && space->port == p->port) { + p->prot_space = space; + return p->prot_space->auth; + } + } + } - return q->auth.auth; + return NULL; } /** @@ -975,7 +1005,7 @@ const char *urldb_get_auth_details(const char *url) bool urldb_get_cert_permissions(const char *url) { struct path_data *p; - struct host_part *h; + const struct host_part *h; assert(url); @@ -985,8 +1015,9 @@ bool urldb_get_cert_permissions(const char *url) for (; p && p->parent; p = p->parent) /* do nothing */; + assert(p); - h = (struct host_part *)p; + h = (const struct host_part *)p; return h->permit_invalid_certs; } @@ -1001,48 +1032,63 @@ bool urldb_get_cert_permissions(const char *url) void urldb_set_auth_details(const char *url, const char *realm, const char *auth) { - struct path_data *p; - char *urlt, *t1, *t2; + struct path_data *p, *pi; + struct host_part *h; + struct prot_space_data *space, *space_alloc; + char *realm_alloc, *auth_alloc, *scheme_alloc; assert(url && realm && auth); - urlt = strdup(url); - if (!urlt) - return; - - /* strip leafname from URL */ - t1 = strrchr(urlt, '/'); - if (t1) { - *(t1 + 1) = '\0'; - } - /* add url, in case it's missing */ - urldb_add_url(urlt); - - p = urldb_find_url(urlt); + urldb_add_url(url); - free(urlt); + p = urldb_find_url(url); if (!p) return; - /** \todo search subtree for same realm/auth details - * and remove them (as the lookup routine searches up the tree) */ - - t1 = strdup(realm); - t2 = strdup(auth); + /* Search for host_part */ + for (pi = p; pi->parent != NULL; pi = pi->parent) + ; + h = (struct host_part *)pi; - if (!t1 || !t2) { - free(t1); - free(t2); - return; + /* Search if given URL belongs to a protection space we already know of. */ + for (space = h->prot_space; space; space = space->next) { + if (!strcmp(space->realm, realm) + && !strcmp(space->scheme, p->scheme) + && space->port == p->port) + break; } - free(p->auth.realm); - free(p->auth.auth); + if (space != NULL) { + /* Overrule existing auth. */ + free(space->auth); + space->auth = strdup(auth); + } else { + /* Create a new protection space. */ + space = space_alloc = malloc(sizeof(struct prot_space_data)); + scheme_alloc = strdup(p->scheme); + realm_alloc = strdup(realm); + auth_alloc = strdup(auth); + + if (!space_alloc || !scheme_alloc + || !realm_alloc || !auth_alloc) { + free(space_alloc); + free(scheme_alloc); + free(realm_alloc); + free(auth_alloc); + return; + } - p->auth.realm = t1; - p->auth.auth = t2; + space->scheme = scheme_alloc; + space->port = p->port; + space->realm = realm_alloc; + space->auth = auth_alloc; + space->next = h->prot_space; + h->prot_space = space; + } + + p->prot_space = space; } /** @@ -1067,6 +1113,7 @@ void urldb_set_cert_permissions(const char *url, bool permit) for (; p && p->parent; p = p->parent) /* do nothing */; + assert(p); h = (struct host_part *)p; @@ -3878,6 +3925,7 @@ void urldb_destroy_host_tree(struct host_part *root) { struct host_part *a, *b; struct path_data *p, *q; + struct prot_space_data *s, *t; /* Destroy children */ for (a = root->children; a; a = b) { @@ -3894,6 +3942,12 @@ void urldb_destroy_host_tree(struct host_part *root) /* Root path */ urldb_destroy_path_node_content(&root->paths); + /* Proctection space data */ + for (s = root->prot_space; s; s = t) { + t = s->next; + urldb_destroy_prot_space(s); + } + /* And ourselves */ free(root->part); free(root); @@ -3955,8 +4009,6 @@ void urldb_destroy_path_node_content(struct path_data *node) bitmap_destroy(node->thumb); free(node->urld.title); - free(node->auth.realm); - free(node->auth.auth); for (a = node->cookies; a; a = b) { b = a->next; @@ -3980,6 +4032,21 @@ void urldb_destroy_cookie(struct cookie_internal_data *c) free(c); } +/** + * Destroy protection space data + * + * \param space Protection space to destroy + */ +void urldb_destroy_prot_space(struct prot_space_data *space) +{ + free(space->scheme); + free(space->realm); + free(space->auth); + + free(space); +} + + /** * Destroy a search tree * diff --git a/content/urldb.h b/content/urldb.h index 0e655dbe1..f31409c41 100644 --- a/content/urldb.h +++ b/content/urldb.h @@ -85,7 +85,7 @@ const char *urldb_get_url(const char *url); /* Authentication modification / lookup */ void urldb_set_auth_details(const char *url, const char *realm, const char *auth); -const char *urldb_get_auth_details(const char *url); +const char *urldb_get_auth_details(const char *url, const char *realm); /* SSL certificate permissions */ void urldb_set_cert_permissions(const char *url, bool permit); diff --git a/riscos/401login.c b/riscos/401login.c index ac3ebe35c..e75fe022c 100644 --- a/riscos/401login.c +++ b/riscos/401login.c @@ -98,6 +98,7 @@ void ro_gui_401login_open(struct browser_window *bw, const char *host, { struct session_401 *session; wimp_w w; + const char *auth; session = calloc(1, sizeof(struct session_401)); if (!session) { @@ -111,10 +112,28 @@ void ro_gui_401login_open(struct browser_window *bw, const char *host, warn_user("NoMemory", 0); return; } - session->uname[0] = '\0'; - session->pwd[0] = '\0'; + if (realm == NULL) + realm = "Secure Area"; + auth = urldb_get_auth_details(session->url, realm); + if (auth == NULL) { + session->uname[0] = '\0'; + session->pwd[0] = '\0'; + } else { + const char *pwd; + size_t pwd_len; + + pwd = strchr(auth, ':'); + assert(pwd && pwd < auth + sizeof(session->uname)); + memcpy(session->uname, auth, pwd - auth); + session->uname[pwd - auth] = '\0'; + ++pwd; + pwd_len = strlen(pwd); + assert(pwd_len < sizeof(session->pwd)); + memcpy(session->pwd, pwd, pwd_len); + session->pwd[pwd_len] = '\0'; + } session->host = strdup(host); - session->realm = strdup(realm ? realm : "Secure Area"); + session->realm = strdup(realm); session->bwin = bw; if ((!session->host) || (!session->realm)) { free(session->host); -- cgit v1.2.3