summaryrefslogtreecommitdiffstats
path: root/WWW/Library/Implementation/HTAABrow.c
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation/HTAABrow.c')
-rw-r--r--WWW/Library/Implementation/HTAABrow.c1354
1 files changed, 1354 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTAABrow.c b/WWW/Library/Implementation/HTAABrow.c
new file mode 100644
index 0000000..c963acd
--- /dev/null
+++ b/WWW/Library/Implementation/HTAABrow.c
@@ -0,0 +1,1354 @@
+/*
+ * $LynxId: HTAABrow.c,v 1.43 2018/05/11 22:54:19 tom Exp $
+ *
+ * MODULE HTAABrow.c
+ * BROWSER SIDE ACCESS AUTHORIZATION MODULE
+ *
+ * Contains the code for keeping track on server hostnames,
+ * port numbers, scheme names, usernames, passwords
+ * (and servers' public keys).
+ *
+ * IMPORTANT:
+ * Routines in this module use dynamic allocation, but free
+ * automatically all the memory reserved by them.
+ *
+ * Therefore the caller never has to (and never should)
+ * free() any object returned by these functions.
+ *
+ * Therefore also all the strings returned by this package
+ * are only valid until the next call to the same function
+ * is made. This approach is selected, because of the nature
+ * of access authorization: no string returned by the package
+ * needs to be valid longer than until the next call.
+ *
+ * This also makes it easy to plug the AA package in:
+ * you don't have to ponder whether to free() something
+ * here or is it done somewhere else (because it is always
+ * done somewhere else).
+ *
+ * The strings that the package needs to store are copied
+ * so the original strings given as parameters to AA
+ * functions may be freed or modified with no side effects.
+ *
+ * The AA package does not free() anything else than what
+ * it has itself allocated.
+ *
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ *
+ * HISTORY:
+ * Oct 17 AL Made corrections suggested by marca:
+ * Added if (!realm->username) return NULL;
+ * Changed some ""s to NULLs.
+ * Now doing calloc() to init uuencode source;
+ * otherwise HTUU_encode() reads uninitialized memory
+ * every now and then (not a real bug but not pretty).
+ * Corrected the formula for uuencode destination size.
+ *
+ * 28 Apr 1997 AJL Do Proxy Authorisation.
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTString.h>
+#include <HTParse.h> /* URL parsing function */
+#include <HTList.h> /* HTList object */
+#include <HTAlert.h> /* HTConfirm(), HTPrompt() */
+#include <HTAAUtil.h> /* AA common to both sides */
+#include <HTAssoc.h> /* Assoc list */
+#include <HTAccess.h> /* Are we using an HTTP gateway? */
+#include <HTAABrow.h> /* Implemented here */
+#include <HTUU.h> /* Uuencoding and uudecoding */
+
+#include <LYLeaks.h>
+
+/*
+ * Local datatype definitions
+ *
+ * HTAAServer contains all the information about one server.
+ */
+typedef struct {
+
+ char *hostname; /* Host's name */
+ int portnumber; /* Port number */
+ BOOL IsProxy; /* Is it a proxy? */
+ HTList *setups; /* List of protection setups
+ on this server; i.e., valid
+ authentication schemes and
+ templates when to use them.
+ This is actually a list of
+ HTAASetup objects. */
+ HTList *realms; /* Information about passwords */
+} HTAAServer;
+
+/*
+ * HTAASetup contains information about one server's one
+ * protected tree of documents.
+ */
+typedef struct {
+ HTAAServer *server; /* Which server serves this tree */
+ char *ctemplate; /* Template for this tree */
+ HTList *valid_schemes; /* Valid authentic.schemes */
+ HTAssocList **scheme_specifics; /* Scheme specific params */
+ BOOL retry; /* Failed last time -- reprompt (or whatever) */
+} HTAASetup;
+
+/*
+ * Information about usernames and passwords in
+ * Basic and Pubkey authentication schemes;
+ */
+typedef struct {
+ char *realmname; /* Password domain name */
+ char *username; /* Username in that domain */
+ char *password; /* Corresponding password */
+} HTAARealm;
+
+/*
+ * To free off all globals. - FM
+ */
+static void free_HTAAGlobals(void);
+static BOOL free_HTAAGlobalsSet = FALSE;
+static char *HTAA_composeAuthResult = NULL;
+static char *compose_auth_stringResult = NULL; /* Uuencoded presentation */
+
+/*
+ * Module-wide global variables
+ */
+static HTList *server_table = NULL; /* Browser's info about servers */
+static char *secret_key = NULL; /* Browser's latest secret key */
+static HTAASetup *current_setup = NULL; /* The server setup we are currently */
+
+ /* talking to */
+static char *current_hostname = NULL; /* The server's name and portnumber */
+static int current_portnumber = 80; /* where we are currently trying to */
+
+ /* connect. */
+static char *current_docname = NULL; /* The document's name we are */
+
+ /* trying to access. */
+static char *HTAAForwardAuth = NULL; /* Authorization: line to forward */
+
+ /* (used by gateway httpds) */
+static HTAASetup *proxy_setup = NULL; /* Same as above, but for Proxy -AJL */
+static char *proxy_hostname = NULL;
+static char *proxy_docname = NULL;
+static int proxy_portnumber = 80;
+
+/*** HTAAForwardAuth for enabling gateway-httpds to forward Authorization ***/
+
+void HTAAForwardAuth_set(const char *scheme_name,
+ const char *scheme_specifics)
+{
+ size_t len = (20
+ + (scheme_name ? strlen(scheme_name) : 0)
+ + (scheme_specifics ? strlen(scheme_specifics) : 0));
+
+ FREE(HTAAForwardAuth);
+ if ((HTAAForwardAuth = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "HTAAForwardAuth_set");
+
+ strcpy(HTAAForwardAuth, "Authorization: ");
+ if (scheme_name) {
+ strcat(HTAAForwardAuth, scheme_name);
+ strcat(HTAAForwardAuth, " ");
+ if (scheme_specifics) {
+ strcat(HTAAForwardAuth, scheme_specifics);
+ }
+ }
+}
+
+void HTAAForwardAuth_reset(void)
+{
+ FREE(HTAAForwardAuth);
+}
+
+/**************************** HTAAServer ***********************************/
+
+static void HTAASetup_delete(HTAASetup * killme); /* Forward */
+
+/* static HTAAServer_new()
+ * ALLOCATE A NEW NODE TO HOLD SERVER INFO
+ * AND ADD IT TO THE LIST OF SERVERS
+ * ON ENTRY:
+ * hostname is the name of the host that the server
+ * is running in.
+ * portnumber is the portnumber which the server listens.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * ON EXIT:
+ * returns the newly-allocated node with all the strings
+ * duplicated.
+ * Strings will be automatically freed by
+ * the function HTAAServer_delete(), which also
+ * frees the node itself.
+ */
+static HTAAServer *HTAAServer_new(const char *hostname,
+ int portnumber,
+ int IsProxy)
+{
+ HTAAServer *server;
+
+ if ((server = typecalloc(HTAAServer)) == 0)
+ outofmem(__FILE__, "HTAAServer_new");
+
+ server->hostname = NULL;
+ server->portnumber = (portnumber > 0 ? portnumber : 80);
+ server->IsProxy = (BOOLEAN) IsProxy;
+ server->setups = HTList_new();
+ server->realms = HTList_new();
+
+ if (hostname)
+ StrAllocCopy(server->hostname, hostname);
+
+ if (!server_table)
+ server_table = HTList_new();
+
+ HTList_addObject(server_table, (void *) server);
+
+ return server;
+}
+
+/* static HTAAServer_delete()
+ *
+ * DELETE THE ENTRY FOR THE SERVER FROM THE HOST TABLE,
+ * AND FREE THE MEMORY USED BY IT.
+ *
+ * ON ENTRY:
+ * killme points to the HTAAServer to be freed.
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAAServer_delete(HTAAServer *killme)
+{
+ int n, i;
+ HTAASetup *setup;
+ HTAARealm *realm;
+ HTList *cur;
+
+ if (killme) {
+ if (killme->setups != NULL) {
+ n = HTList_count(killme->setups);
+ for (i = (n - 1); i >= 0; i--) {
+ if ((setup = (HTAASetup *) HTList_objectAt(killme->setups,
+ i)) != NULL) {
+ HTAASetup_delete(setup);
+ setup = NULL;
+ }
+ }
+ HTList_delete(killme->setups);
+ killme->setups = NULL;
+ }
+
+ cur = killme->realms;
+ while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
+ FREE(realm->realmname);
+ FREE(realm->username);
+ FREE(realm->password);
+ FREE(realm);
+ }
+ HTList_delete(killme->realms);
+ killme->realms = NULL;
+
+ FREE(killme->hostname);
+
+ HTList_removeObject(server_table, (void *) killme);
+ FREE(killme);
+ }
+}
+
+/* static HTAAServer_lookup()
+ * LOOK UP SERVER BY HOSTNAME AND PORTNUMBER
+ * ON ENTRY:
+ * hostname obvious.
+ * portnumber if non-positive defaults to 80.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * Looks up the server in the module-global server_table.
+ *
+ * ON EXIT:
+ * returns pointer to a HTAAServer structure
+ * representing the looked-up server.
+ * NULL, if not found.
+ */
+static HTAAServer *HTAAServer_lookup(const char *hostname,
+ int portnumber,
+ int IsProxy)
+{
+ if (hostname) {
+ HTList *cur = server_table;
+ HTAAServer *server;
+
+ if (portnumber <= 0)
+ portnumber = 80;
+
+ while (NULL != (server = (HTAAServer *) HTList_nextObject(cur))) {
+ if (server->portnumber == portnumber &&
+ 0 == strcmp(server->hostname, hostname) &&
+ server->IsProxy == IsProxy)
+ return server;
+ }
+ }
+ return NULL; /* NULL parameter, or not found */
+}
+
+/*************************** HTAASetup *******************************/
+
+/* static HTAASetup_lookup()
+ * FIGURE OUT WHICH AUTHENTICATION SETUP THE SERVER
+ * IS USING FOR A GIVEN FILE ON A GIVEN HOST AND PORT
+ *
+ * ON ENTRY:
+ * hostname is the name of the server host machine.
+ * portnumber is the port that the server is running in.
+ * docname is the (URL-)pathname of the document we
+ * are trying to access.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * This function goes through the information known about
+ * all the setups of the server, and finds out if the given
+ * filename resides in one of the protected directories.
+ *
+ * ON EXIT:
+ * returns NULL if no match.
+ * Otherwise, a HTAASetup structure representing
+ * the protected server setup on the corresponding
+ * document tree.
+ *
+ */
+static HTAASetup *HTAASetup_lookup(const char *hostname,
+ int portnumber,
+ const char *docname,
+ int IsProxy)
+{
+ HTAAServer *server;
+ HTAASetup *setup;
+
+ if (portnumber <= 0)
+ portnumber = 80;
+
+ if (hostname && docname && *hostname && *docname &&
+ NULL != (server = HTAAServer_lookup(hostname,
+ portnumber,
+ IsProxy))) {
+
+ HTList *cur = server->setups;
+
+ CTRACE((tfp, "%s %s (%s:%d:%s)\n",
+ "HTAASetup_lookup: resolving setup for",
+ (IsProxy ? "proxy" : "server"),
+ hostname, portnumber, docname));
+
+ while (NULL != (setup = (HTAASetup *) HTList_nextObject(cur))) {
+ if (HTAA_templateMatch(setup->ctemplate, docname)) {
+ CTRACE((tfp, "%s `%s' %s `%s'\n",
+ "HTAASetup_lookup:", docname,
+ "matched template", setup->ctemplate));
+ return setup;
+ } else {
+ CTRACE((tfp, "%s `%s' %s `%s'\n",
+ "HTAASetup_lookup:", docname,
+ "did NOT match template", setup->ctemplate));
+ }
+ } /* while setups remain */
+ }
+ /* if valid parameters and server found */
+ CTRACE((tfp, "%s `%s' %s\n",
+ "HTAASetup_lookup: No template matched",
+ NONNULL(docname),
+ "(so probably not protected)"));
+
+ return NULL; /* NULL in parameters, or not found */
+}
+
+/* static HTAASetup_new()
+ * CREATE A NEW SETUP NODE
+ * ON ENTRY:
+ * server is a pointer to a HTAAServer structure
+ * to which this setup belongs.
+ * ctemplate documents matching this template
+ * are protected according to this setup.
+ * valid_schemes a list containing all valid authentication
+ * schemes for this setup.
+ * If NULL, all schemes are disallowed.
+ * scheme_specifics is an array of assoc lists, which
+ * contain scheme specific parameters given
+ * by server in Authenticate: fields.
+ * If NULL, all scheme specifics are
+ * set to NULL.
+ * ON EXIT:
+ * returns a new HTAASetup node, and also adds it as
+ * part of the HTAAServer given as parameter.
+ */
+static HTAASetup *HTAASetup_new(HTAAServer *server, char *ctemplate,
+ HTList *valid_schemes,
+ HTAssocList **scheme_specifics)
+{
+ HTAASetup *setup;
+
+ if (!server || isEmpty(ctemplate))
+ return NULL;
+
+ if ((setup = typecalloc(HTAASetup)) == 0)
+ outofmem(__FILE__, "HTAASetup_new");
+
+ setup->retry = NO;
+ setup->server = server;
+ setup->ctemplate = NULL;
+ if (ctemplate)
+ StrAllocCopy(setup->ctemplate, ctemplate);
+ setup->valid_schemes = valid_schemes;
+ setup->scheme_specifics = scheme_specifics;
+
+ HTList_addObject(server->setups, (void *) setup);
+
+ return setup;
+}
+
+/* static HTAASetup_delete()
+ * FREE A HTAASetup STRUCTURE
+ * ON ENTRY:
+ * killme is a pointer to the structure to free().
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAASetup_delete(HTAASetup * killme)
+{
+ int scheme;
+
+ if (killme) {
+ FREE(killme->ctemplate);
+ if (killme->valid_schemes) {
+ HTList_delete(killme->valid_schemes);
+ killme->valid_schemes = NULL;
+ }
+ for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++)
+ if (killme->scheme_specifics[scheme])
+ HTAssocList_delete(killme->scheme_specifics[scheme]);
+ FREE(killme->scheme_specifics);
+ FREE(killme);
+ }
+}
+
+/* static HTAASetup_updateSpecifics()
+ * COPY SCHEME SPECIFIC PARAMETERS
+ * TO HTAASetup STRUCTURE
+ * ON ENTRY:
+ * setup destination setup structure.
+ * specifics string array containing scheme
+ * specific parameters for each scheme.
+ * If NULL, all the scheme specific
+ * parameters are set to NULL.
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAASetup_updateSpecifics(HTAASetup * setup, HTAssocList **specifics)
+{
+ int scheme;
+
+ if (setup) {
+ if (setup->scheme_specifics) {
+ for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++) {
+ if (setup->scheme_specifics[scheme])
+ HTAssocList_delete(setup->scheme_specifics[scheme]);
+ }
+ FREE(setup->scheme_specifics);
+ }
+ setup->scheme_specifics = specifics;
+ }
+}
+
+/*************************** HTAARealm **********************************/
+
+/* static HTAARealm_lookup()
+ * LOOKUP HTAARealm STRUCTURE BY REALM NAME
+ * ON ENTRY:
+ * realm_table a list of realm objects.
+ * realmname is the name of realm to look for.
+ *
+ * ON EXIT:
+ * returns the realm. NULL, if not found.
+ */
+static HTAARealm *HTAARealm_lookup(HTList *realm_table,
+ const char *realmname)
+{
+ if (realm_table && realmname) {
+ HTList *cur = realm_table;
+ HTAARealm *realm;
+
+ while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
+ if (0 == strcmp(realm->realmname, realmname))
+ return realm;
+ }
+ }
+ return NULL; /* No table, NULL param, or not found */
+}
+
+/* static HTAARealm_new()
+ * CREATE A NODE CONTAINING USERNAME AND
+ * PASSWORD USED FOR THE GIVEN REALM.
+ * IF REALM ALREADY EXISTS, CHANGE
+ * USERNAME/PASSWORD.
+ * ON ENTRY:
+ * realm_table a list of realms to where to add
+ * the new one, too.
+ * realmname is the name of the password domain.
+ * username and
+ * password are what you can expect them to be.
+ *
+ * ON EXIT:
+ * returns the created realm.
+ */
+static HTAARealm *HTAARealm_new(HTList *realm_table,
+ const char *realmname,
+ const char *username,
+ const char *password)
+{
+ HTAARealm *realm;
+
+ realm = HTAARealm_lookup(realm_table, realmname);
+
+ if (!realm) {
+ if ((realm = typecalloc(HTAARealm)) == 0)
+ outofmem(__FILE__, "HTAARealm_new");
+
+ realm->realmname = NULL;
+ realm->username = NULL;
+ realm->password = NULL;
+ StrAllocCopy(realm->realmname, realmname);
+ if (realm_table)
+ HTList_addObject(realm_table, (void *) realm);
+ }
+ if (username)
+ StrAllocCopy(realm->username, username);
+ if (password)
+ StrAllocCopy(realm->password, password);
+
+ return realm;
+}
+
+BOOL HTAA_HaveUserinfo(const char *hostname)
+{
+ int gen_delims = 0;
+ BOOL result = FALSE;
+ char *my_info = NULL;
+
+ if (StrAllocCopy(my_info, hostname) != NULL) {
+ char *at_sign = HTSkipToAt(my_info, &gen_delims);
+
+ free(my_info);
+ if (at_sign != NULL && gen_delims == 0)
+ result = TRUE;
+ }
+ return result;
+}
+
+/*
+ * If there is userinfo in the hostname string, update the realm to use that
+ * information. The command-line "-auth" option will override this.
+ */
+static void fill_in_userinfo(HTAARealm *realm, const char *hostname)
+{
+ int gen_delims = 0;
+ char *my_info = NULL;
+ char *at_sign = HTSkipToAt(StrAllocCopy(my_info, hostname), &gen_delims);
+
+ if (at_sign != NULL && gen_delims == 0) {
+ char *colon;
+
+ *at_sign = '\0';
+ if ((colon = StrChr(my_info, ':')) != 0) {
+ *colon++ = '\0';
+ }
+ if (non_empty(my_info)) {
+ char *msg;
+ BOOL prior = non_empty(realm->username);
+
+ if (prior && strcmp(realm->username, my_info)) {
+ msg = 0;
+ HTSprintf0(&msg,
+ gettext("username for realm %s changed from %s to %s"),
+ realm->realmname,
+ realm->username,
+ my_info);
+ HTAlert(msg);
+ free(msg);
+ FREE(realm->username);
+ StrAllocCopy(realm->username, my_info);
+ } else if (!prior) {
+ StrAllocCopy(realm->username, my_info);
+ }
+ if (non_empty(colon)) {
+ prior = non_empty(realm->password);
+ if (prior && strcmp(realm->password, colon)) {
+ msg = 0;
+ HTSprintf0(&msg,
+ gettext("password for realm %s user %s changed"),
+ realm->realmname,
+ realm->username);
+ HTAlert(msg);
+ free(msg);
+ FREE(realm->password);
+ StrAllocCopy(realm->password, colon);
+ } else if (!prior) {
+ StrAllocCopy(realm->password, colon);
+ }
+ }
+ }
+ }
+ free(my_info);
+}
+
+/***************** Basic and Pubkey Authentication ************************/
+
+/* static compose_auth_string()
+ *
+ * COMPOSE Basic OR Pubkey AUTHENTICATION STRING;
+ * PROMPTS FOR USERNAME AND PASSWORD IF NEEDED
+ *
+ * ON ENTRY:
+ * hostname may include user- and password information
+ * scheme is either HTAA_BASIC or HTAA_PUBKEY.
+ * setup is the current server setup.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * ON EXIT:
+ * returns a newly composed authorization string,
+ * (with, of course, a newly generated secret
+ * key and fresh timestamp, if Pubkey-scheme
+ * is being used).
+ * NULL, if something fails.
+ * NOTE:
+ * Like throughout the entire AA package, no string or structure
+ * returned by AA package needs to (or should) be freed.
+ *
+ */
+static char *compose_auth_string(const char *hostname,
+ HTAAScheme scheme,
+ HTAASetup * setup,
+ int IsProxy)
+{
+ char *cleartext = NULL; /* Cleartext presentation */
+ char *ciphertext = NULL; /* Encrypted presentation */
+ size_t len;
+ char *msg = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ char *realmname = NULL;
+ char *theHost = NULL;
+ char *proxiedHost = NULL;
+ char *thePort = NULL;
+ HTAARealm *realm;
+ const char *i_net_addr = "0.0.0.0"; /* Change... @@@@ */
+ const char *timestamp = "42"; /* ... these @@@@ */
+
+ FREE(compose_auth_stringResult); /* From previous call */
+
+ if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY) ||
+ !(setup &&
+ setup->scheme_specifics &&
+ setup->scheme_specifics[scheme] &&
+ setup->server &&
+ setup->server->realms))
+ return NULL;
+
+ realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm");
+ if (!realmname)
+ return NULL;
+
+ realm = HTAARealm_lookup(setup->server->realms, realmname);
+ setup->retry |= HTAA_HaveUserinfo(hostname);
+
+ if (!(realm &&
+ non_empty(realm->username) &&
+ non_empty(realm->password)) || setup->retry) {
+ if (!realm) {
+ CTRACE((tfp, "%s `%s' %s\n",
+ "compose_auth_string: realm:", realmname,
+ "not found -- creating"));
+ realm = HTAARealm_new(setup->server->realms,
+ realmname, NULL, NULL);
+ }
+ fill_in_userinfo(realm, hostname);
+ /*
+ * The template should be either the '*' global for everything on the
+ * server (always true for proxy authorization setups), or a path for
+ * the start of a protected limb, with no host field, but we'll check
+ * for a host anyway in case a WWW-Protection-Template header set an
+ * absolute URL instead of a path. If we do get a host from this, it
+ * will include the port. - FM
+ */
+ if ((!IsProxy) && using_proxy && setup->ctemplate) {
+ proxiedHost = HTParse(setup->ctemplate, "", PARSE_HOST);
+ if (proxiedHost && *proxiedHost != '\0') {
+ theHost = proxiedHost;
+ }
+ }
+ /*
+ * If we didn't get a host field from the template, set up the host
+ * name and port from the setup->server elements. - FM
+ */
+ if (!theHost)
+ theHost = setup->server->hostname;
+ if (setup->server->portnumber > 0 &&
+ setup->server->portnumber != 80) {
+ HTSprintf0(&thePort, ":%d", setup->server->portnumber);
+ }
+
+ HTSprintf0(&msg, gettext("Username for '%s' at %s '%s%s':"),
+ realm->realmname,
+ (IsProxy ? "proxy" : "server"),
+ (theHost ? theHost : "??"),
+ NonNull(thePort));
+ FREE(proxiedHost);
+ FREE(thePort);
+ if (non_empty(realm->username)) {
+ StrAllocCopy(username, realm->username);
+ }
+ if (non_empty(realm->password)) {
+ StrAllocCopy(password, realm->password);
+ }
+ HTPromptUsernameAndPassword(msg, &username, &password, IsProxy);
+
+ FREE(msg);
+ FREE(realm->username);
+ FREE(realm->password);
+
+ realm->username = username;
+ realm->password = password;
+
+ if (!realm->username || !realm->password) {
+ /*
+ * Signals to retry. - FM
+ */
+ return NULL;
+ } else if (*realm->username == '\0') {
+ /*
+ * Signals to abort. - FM
+ */
+ StrAllocCopy(compose_auth_stringResult, "");
+ return compose_auth_stringResult;
+ }
+ }
+
+ len = (strlen(NonNull(realm->username)) +
+ strlen(NonNull(realm->password)) + 3);
+
+ if (scheme == HTAA_PUBKEY) {
+#ifdef PUBKEY
+ /* Generate new secret key */
+ StrAllocCopy(secret_key, HTAA_generateRandomKey());
+#endif /* PUBKEY */
+ /* Room for secret key, timestamp and inet address */
+ len += strlen(NonNull(secret_key)) + 30;
+ } else {
+ FREE(secret_key);
+ }
+
+ if ((cleartext = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "compose_auth_string");
+
+ if (realm->username)
+ strcpy(cleartext, realm->username);
+ else
+ *cleartext = '\0';
+
+ strcat(cleartext, ":");
+
+ if (realm->password)
+ strcat(cleartext, realm->password);
+
+ if (scheme == HTAA_PUBKEY) {
+ strcat(cleartext, ":");
+ strcat(cleartext, i_net_addr);
+ strcat(cleartext, ":");
+ strcat(cleartext, timestamp);
+ strcat(cleartext, ":");
+ if (secret_key)
+ strcat(cleartext, secret_key);
+
+ if (!((ciphertext = typecallocn(char, 2 * len)) &&
+ (compose_auth_stringResult = typecallocn(char, 3 * len))))
+ outofmem(__FILE__, "compose_auth_string");
+
+#ifdef PUBKEY
+ HTPK_encrypt(cleartext, ciphertext, server->public_key);
+ HTUU_encode((unsigned char *) ciphertext, strlen(ciphertext),
+ compose_auth_stringResult);
+#endif /* PUBKEY */
+ FREE(cleartext);
+ FREE(ciphertext);
+ } else { /* scheme == HTAA_BASIC */
+ if (!(compose_auth_stringResult =
+ typecallocn(char, (4 * ((len + 2) / 3)) + 1)))
+ outofmem(__FILE__, "compose_auth_string");
+
+ HTUU_encode((unsigned char *) cleartext, strlen(cleartext),
+ compose_auth_stringResult);
+ FREE(cleartext);
+ }
+ return compose_auth_stringResult;
+}
+
+/* BROWSER static HTAA_selectScheme()
+ * SELECT THE AUTHENTICATION SCHEME TO USE
+ * ON ENTRY:
+ * setup is the server setup structure which can
+ * be used to make the decision about the
+ * used scheme.
+ *
+ * When new authentication methods are added to library
+ * this function makes the decision about which one to
+ * use at a given time. This can be done by inspecting
+ * environment variables etc.
+ *
+ * Currently only searches for the first valid scheme,
+ * and if nothing found suggests Basic scheme;
+ *
+ * ON EXIT:
+ * returns the authentication scheme to use.
+ */
+static HTAAScheme HTAA_selectScheme(HTAASetup * setup)
+{
+ int scheme;
+
+ if (setup && setup->valid_schemes) {
+ for (scheme = HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++) {
+ void *object = (void *) (intptr_t) scheme;
+
+ if (-1 < HTList_indexOf(setup->valid_schemes, object))
+ return (HTAAScheme) scheme;
+ }
+ }
+ return HTAA_BASIC;
+}
+
+/*
+ * Purpose: Free off all module globals.
+ * Arguments: void
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * To be used at program exit.
+ * Revision History:
+ * 06-19-96 created - FM
+ */
+static void free_HTAAGlobals(void)
+{
+ HTAAServer *server;
+ int n, i;
+
+ if (server_table != NULL) {
+ n = HTList_count(server_table);
+ for (i = (n - 1); i >= 0; i--) {
+ if ((server = (HTAAServer *) HTList_objectAt(server_table,
+ i)) != NULL) {
+ HTAAServer_delete(server);
+ server = NULL;
+ }
+ }
+ HTList_delete(server_table);
+ server_table = NULL;
+ }
+
+ HTAAForwardAuth_reset();
+ FREE(HTAA_composeAuthResult);
+ FREE(current_hostname);
+ FREE(current_docname);
+ FREE(proxy_hostname);
+ FREE(proxy_docname);
+ FREE(compose_auth_stringResult);
+ FREE(secret_key);
+}
+
+/* BROWSER PUBLIC HTAA_composeAuth()
+ *
+ * SELECT THE AUTHENTICATION SCHEME AND
+ * COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE
+ * IF WE ALREADY KNOW THAT THE HOST REQUIRES AUTHENTICATION
+ *
+ * ON ENTRY:
+ * hostname is the hostname of the server.
+ * portnumber is the portnumber in which the server runs.
+ * docname is the pathname of the document (as in URL)
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * ON EXIT:
+ * returns NULL, if no authorization seems to be needed, or
+ * if it is the entire Authorization: line, e.g.
+ *
+ * "Authorization: Basic username:password"
+ *
+ * As usual, this string is automatically freed.
+ */
+char *HTAA_composeAuth(const char *hostname,
+ const int portnumber,
+ const char *docname,
+ int IsProxy)
+{
+ char *auth_string;
+ BOOL retry;
+ HTAAScheme scheme;
+ size_t len;
+
+ /*
+ * Setup atexit() freeing if not done already. - FM
+ */
+ if (!free_HTAAGlobalsSet) {
+#ifdef LY_FIND_LEAKS
+ atexit(free_HTAAGlobals);
+#endif
+ free_HTAAGlobalsSet = TRUE;
+ }
+
+ /*
+ * Make gateway httpds pass authorization field as it was received. (This
+ * still doesn't really work because Authenticate: headers from remote
+ * server are not forwarded to client yet so it cannot really know that it
+ * should send authorization; I will not implement it yet because I feel we
+ * will soon change radically the way requests are represented to allow
+ * multithreading on server-side. Life is hard.)
+ */
+ if (HTAAForwardAuth) {
+ CTRACE((tfp, "HTAA_composeAuth: %s\n",
+ "Forwarding received authorization"));
+ StrAllocCopy(HTAA_composeAuthResult, HTAAForwardAuth);
+ HTAAForwardAuth_reset(); /* Just a precaution */
+ return HTAA_composeAuthResult;
+ }
+
+ FREE(HTAA_composeAuthResult); /* From previous call */
+
+ if (IsProxy) {
+ /*
+ * Proxy Authorization required. - AJL
+ */
+
+ CTRACE((tfp, "Composing Proxy Authorization for %s:%d/%s\n",
+ hostname, portnumber, docname));
+
+ if (proxy_portnumber != portnumber ||
+ !proxy_hostname || !proxy_docname ||
+ !hostname || !docname ||
+ 0 != strcmp(proxy_hostname, hostname) ||
+ 0 != strcmp(proxy_docname, docname)) {
+
+ retry = NO;
+
+ proxy_portnumber = portnumber;
+
+ if (hostname)
+ StrAllocCopy(proxy_hostname, hostname);
+ else
+ FREE(proxy_hostname);
+
+ if (docname)
+ StrAllocCopy(proxy_docname, docname);
+ else
+ FREE(proxy_docname);
+ } else {
+ retry = YES;
+ }
+
+ if (!proxy_setup || !retry)
+ proxy_setup = HTAASetup_lookup(hostname, portnumber,
+ docname, IsProxy);
+
+ if (!proxy_setup)
+ return NULL;
+
+ switch (scheme = HTAA_selectScheme(proxy_setup)) {
+ case HTAA_BASIC:
+ case HTAA_PUBKEY:
+ auth_string = compose_auth_string(hostname, scheme, proxy_setup, IsProxy);
+ break;
+ case HTAA_KERBEROS_V4:
+ /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
+ default:
+ {
+ char *msg = NULL;
+
+ HTSprintf0(&msg, "%s `%s'",
+ gettext("This client doesn't know how to compose proxy authorization information for scheme"),
+ HTAAScheme_name(scheme));
+ HTAlert(msg);
+ FREE(msg);
+ auth_string = NULL;
+ }
+ } /* switch scheme */
+
+ proxy_setup->retry = NO;
+
+ if (!auth_string)
+ /*
+ * Signal a failure. - FM
+ */
+ return NULL; /* Added by marca. */
+ if (*auth_string == '\0') {
+ /*
+ * Signal an abort. - FM
+ */
+ StrAllocCopy(HTAA_composeAuthResult, "");
+ return (HTAA_composeAuthResult);
+ }
+ len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 26;
+ if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "HTAA_composeAuth");
+
+ strcpy(HTAA_composeAuthResult, "Proxy-Authorization: ");
+
+ } else {
+ /*
+ * Normal WWW authorization.
+ */
+ CTRACE((tfp, "Composing Authorization for %s:%d/%s\n",
+ hostname, portnumber, docname));
+
+ if (current_portnumber != portnumber ||
+ !current_hostname || !current_docname ||
+ !hostname || !docname ||
+ 0 != strcmp(current_hostname, hostname) ||
+ 0 != strcmp(current_docname, docname)) {
+
+ retry = NO;
+
+ current_portnumber = portnumber;
+
+ if (hostname)
+ StrAllocCopy(current_hostname, hostname);
+ else
+ FREE(current_hostname);
+
+ if (docname)
+ StrAllocCopy(current_docname, docname);
+ else
+ FREE(current_docname);
+ } else {
+ retry = YES;
+ }
+
+ if (!current_setup || !retry)
+ current_setup = HTAASetup_lookup(hostname, portnumber,
+ docname, IsProxy);
+
+ if (!current_setup)
+ return NULL;
+
+ switch (scheme = HTAA_selectScheme(current_setup)) {
+ case HTAA_BASIC:
+ case HTAA_PUBKEY:
+ auth_string = compose_auth_string(hostname, scheme, current_setup, IsProxy);
+ break;
+ case HTAA_KERBEROS_V4:
+ /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
+ default:
+ {
+ char *msg = 0;
+
+ HTSprintf0(&msg, "%s `%s'",
+ gettext("This client doesn't know how to compose authorization information for scheme"),
+ HTAAScheme_name(scheme));
+ HTAlert(msg);
+ FREE(msg);
+ auth_string = NULL;
+ }
+ } /* switch scheme */
+
+ current_setup->retry = NO;
+
+ if (!auth_string)
+ /*
+ * Signal a failure. - FM
+ */
+ return NULL; /* Added by marca. */
+ if (*auth_string == '\0') {
+ /*
+ * Signal an abort. - FM
+ */
+ StrAllocCopy(HTAA_composeAuthResult, "");
+ return (HTAA_composeAuthResult);
+ }
+
+ len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 20;
+ if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "HTAA_composeAuth");
+
+ strcpy(HTAA_composeAuthResult, "Authorization: ");
+ }
+
+ strcat(HTAA_composeAuthResult, HTAAScheme_name(scheme));
+ strcat(HTAA_composeAuthResult, " ");
+ strcat(HTAA_composeAuthResult, auth_string);
+ return HTAA_composeAuthResult;
+}
+
+/* BROWSER PUBLIC HTAA_shouldRetryWithAuth()
+ *
+ * DETERMINES IF WE SHOULD RETRY THE SERVER
+ * WITH AUTHORIZATION
+ * (OR IF ALREADY RETRIED, WITH A DIFFERENT
+ * USERNAME AND/OR PASSWORD (IF MISSPELLED))
+ * ON ENTRY:
+ * start_of_headers is the first block already read from socket,
+ * but status line skipped; i.e., points to the
+ * start of the header section.
+ * length is the remaining length of the first block.
+ * soc is the socket to read the rest of server reply.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * This function should only be called when
+ * server has replied with a 401 (Unauthorized)
+ * status code.
+ * ON EXIT:
+ * returns YES, if connection should be retried.
+ * The node containing all the necessary
+ * information is
+ * * either constructed if it does not exist
+ * * or password is reset to NULL to indicate
+ * that username and password should be
+ * reprompted when composing Authorization:
+ * field (in function HTAA_composeAuth()).
+ * NO, otherwise.
+ */
+BOOL HTAA_shouldRetryWithAuth(char *start_of_headers,
+ size_t length,
+ int soc,
+ int IsProxy)
+{
+ HTAAScheme scheme;
+ char *line = NULL;
+ int num_schemes = 0;
+ HTList *valid_schemes = HTList_new();
+ HTAssocList **scheme_specifics = NULL;
+ char *ctemplate = NULL;
+ char *temp = NULL;
+ BOOL result = NO;
+
+ /*
+ * Setup atexit() freeing if not done already. - FM
+ */
+ if (!free_HTAAGlobalsSet) {
+#ifdef LY_FIND_LEAKS
+ atexit(free_HTAAGlobals);
+#endif
+ free_HTAAGlobalsSet = TRUE;
+ }
+
+ /*
+ * Read server reply header lines
+ */
+ CTRACE((tfp, "Server reply header lines:\n"));
+
+ HTAA_setupReader(start_of_headers, length, soc);
+ while (NULL != (line = HTAA_getUnfoldedLine()) && *line != '\0') {
+ CTRACE((tfp, "%s\n", line));
+
+ if (StrChr(line, ':')) { /* Valid header line */
+
+ char *p = line;
+ char *fieldname = HTNextField(&p);
+ char *arg1 = HTNextField(&p);
+ char *args = p;
+
+ if ((IsProxy &&
+ 0 == strcasecomp(fieldname, "Proxy-Authenticate:")) ||
+ (!IsProxy &&
+ 0 == strcasecomp(fieldname, "WWW-Authenticate:"))) {
+ if (isEmpty(arg1) || isEmpty(args)) {
+ HTSprintf0(&temp, gettext("Invalid header '%s%s%s%s%s'"), line,
+ (non_empty(arg1) ? " " : ""),
+ NonNull(arg1),
+ (non_empty(args) ? " " : ""),
+ NonNull(args));
+ HTAlert(temp);
+ FREE(temp);
+ } else if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
+ HTList_addObject(valid_schemes, (void *) scheme);
+ if (!scheme_specifics) {
+ int i;
+
+ scheme_specifics =
+ typecallocn(HTAssocList *, HTAA_MAX_SCHEMES);
+
+ if (!scheme_specifics)
+ outofmem(__FILE__, "HTAA_shouldRetryWithAuth");
+
+ for (i = 0; i < HTAA_MAX_SCHEMES; i++)
+ scheme_specifics[i] = NULL;
+ }
+ scheme_specifics[scheme] = HTAA_parseArgList(args);
+ num_schemes++;
+ } else {
+ CTRACE((tfp, "Unknown scheme `%s' %s\n",
+ NONNULL(arg1),
+ (IsProxy ?
+ "in Proxy-Authenticate: field" :
+ "in WWW-Authenticate: field")));
+ }
+ }
+
+ else if (!IsProxy &&
+ 0 == strcasecomp(fieldname, "WWW-Protection-Template:")) {
+ CTRACE((tfp, "Protection template set to `%s'\n", arg1));
+ StrAllocCopy(ctemplate, arg1);
+ }
+
+ } else {
+ CTRACE((tfp, "Invalid header line `%s' ignored\n", line));
+ }
+
+ FREE(line);
+ } /* while header lines remain */
+ FREE(line);
+
+ /*
+ * So should we retry with authorization?
+ */
+ if (IsProxy) {
+ if (num_schemes == 0) {
+ /*
+ * No proxy authorization valid
+ */
+ proxy_setup = NULL;
+ result = NO;
+ }
+ /*
+ * Doing it for proxy. -AJL
+ */
+ else if (proxy_setup && proxy_setup->server) {
+ /*
+ * We have already tried with proxy authorization. Either we don't
+ * have access or username or password was misspelled.
+ *
+ * Update scheme-specific parameters (in case they have expired by
+ * chance).
+ */
+ HTAASetup_updateSpecifics(proxy_setup, scheme_specifics);
+
+ if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
+ proxy_setup = NULL;
+ result = NO;
+ } else {
+ /*
+ * Re-ask username+password (if misspelled).
+ */
+ HTList_delete(valid_schemes);
+ proxy_setup->retry = YES;
+ result = YES;
+ }
+ } else {
+ /*
+ * proxy_setup == NULL, i.e., we have a first connection to a
+ * protected server or the server serves a wider set of documents
+ * than we expected so far.
+ */
+ HTAAServer *server = HTAAServer_lookup(proxy_hostname,
+ proxy_portnumber,
+ IsProxy);
+
+ if (!server) {
+ server = HTAAServer_new(proxy_hostname,
+ proxy_portnumber,
+ IsProxy);
+ }
+ if (!ctemplate) /* Proxy matches everything -AJL */
+ StrAllocCopy(ctemplate, "*");
+ proxy_setup = HTAASetup_new(server,
+ ctemplate,
+ valid_schemes,
+ scheme_specifics);
+ FREE(ctemplate);
+
+ HTAlert(gettext("Proxy authorization required -- retrying"));
+ result = YES;
+ }
+ }
+ /*
+ * Normal WWW authorization.
+ */
+ else if (num_schemes == 0) {
+ /*
+ * No authorization valid.
+ */
+ current_setup = NULL;
+ result = NO;
+ } else if (current_setup && current_setup->server) {
+ /*
+ * So we have already tried with WWW authorization. Either we don't
+ * have access or username or password was misspelled.
+ *
+ * Update scheme-specific parameters (in case they have expired by
+ * chance).
+ */
+ HTAASetup_updateSpecifics(current_setup, scheme_specifics);
+
+ if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
+ current_setup = NULL;
+ result = NO;
+ } else {
+ /*
+ * Re-ask username+password (if misspelled).
+ */
+ current_setup->retry = YES;
+ result = YES;
+ }
+ } else {
+ /*
+ * current_setup == NULL, i.e., we have a first connection to a
+ * protected server or the server serves a wider set of documents than
+ * we expected so far.
+ */
+ HTAAServer *server = HTAAServer_lookup(current_hostname,
+ current_portnumber,
+ IsProxy);
+
+ if (!server) {
+ server = HTAAServer_new(current_hostname,
+ current_portnumber,
+ IsProxy);
+ }
+ if (!ctemplate)
+ ctemplate = HTAA_makeProtectionTemplate(current_docname);
+ current_setup = HTAASetup_new(server,
+ ctemplate,
+ valid_schemes,
+ scheme_specifics);
+ FREE(ctemplate);
+
+ HTAlert(gettext("Access without authorization denied -- retrying"));
+ result = YES;
+ }
+
+ if (result == NO) {
+ HTList_delete(valid_schemes);
+ }
+ return result;
+}
+
+/*
+ * This function clears all authorization information by
+ * invoking the free_HTAAGlobals() function, which normally
+ * is invoked at exit. It allows a browser command to do
+ * this at any time, for example, if the user is leaving
+ * the terminal for a period of time, but does not want
+ * to end the current session. - FM
+ */
+void HTClearHTTPAuthInfo(void)
+{
+ /*
+ * Need code to check cached documents against the protection templates,
+ * and do something to ensure that any protected documents no longer can be
+ * accessed without a new retrieval. - FM
+ */
+
+ /*
+ * Now free all of the authorization info, and reset the
+ * free_HTAAGlobalsSet flag. - FM
+ */
+ free_HTAAGlobals();
+ free_HTAAGlobalsSet = FALSE;
+}