diff options
Diffstat (limited to '')
-rw-r--r-- | WWW/Library/Implementation/HTAABrow.c | 1354 |
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; +} |