/*
* $LynxId: HTAABrow.c,v 1.32 2010/06/17 00:37:22 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)
{
unsigned 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");
assert(HTAAForwardAuth != 0);
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,
BOOL IsProxy)
{
HTAAServer *server;
if ((server = typecalloc(HTAAServer)) == 0)
outofmem(__FILE__, "HTAAServer_new");
assert(server != NULL);
server->hostname = NULL;
server->portnumber = (portnumber > 0 ? portnumber : 80);
server->IsProxy = 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,
BOOL 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,
BOOL 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");
assert(setup != NULL);
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");
assert(realm != NULL);
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;
}
/***************** Basic and Pubkey Authentication ************************/
/* static compose_auth_string()
*
* COMPOSE Basic OR Pubkey AUTHENTICATION STRING;
* PROMPTS FOR USERNAME AND PASSWORD IF NEEDED
*
* ON ENTRY:
* 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(HTAAScheme scheme, HTAASetup * setup, BOOL IsProxy)
{
char *cleartext = NULL; /* Cleartext presentation */
char *ciphertext = NULL; /* Encrypted presentation */
unsigned 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);
if (!(realm &&
realm->username && *realm->username &&
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);
}
/*
* 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);
}
/*
* Set up the message for the username prompt, and then issue the
* prompt. The default username is included in the call to the
* prompting function, but the password is NULL-ed and always replaced.
* - FM
*/
HTSprintf0(&msg, gettext("Username for '%s' at %s '%s%s':"),
realm->realmname,
(IsProxy ? "proxy" : "server"),
(theHost ? theHost : "??"),
NonNull(thePort));
FREE(proxiedHost);
FREE(thePort);
username = realm->username;
password = NULL;
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");
assert(cleartext != NULL);
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 *) 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,
BOOL IsProxy)
{
char *auth_string;
BOOL retry;
HTAAScheme scheme;
unsigned 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(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");
assert(HTAA_composeAuthResult != NULL);
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(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");
assert(HTAA_composeAuthResult != NULL);
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,
BOOL 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;
/*
* 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");
assert(scheme_specifics != NULL);
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;
return NO;
}
/*
* Doing it for proxy. -AJL
*/
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;
return NO;
} else {
/*
* Re-ask username+password (if misspelled).
*/
proxy_setup->retry = YES;
return 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"));
return YES;
}
/* Never reached */
}
/*
* Normal WWW authorization.
*/
if (num_schemes == 0) {
/*
* No authorization valid.
*/
current_setup = NULL;
return NO;
}
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;
return NO;
} else {
/*
* Re-ask username+password (if misspelled).
*/
current_setup->retry = YES;
return 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"));
return YES;
}
/* Never reached */
}
/*
* 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;
}