/* * $LynxId: HTAABrow.c,v 1.29 2009/01/03 00:55:16 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 #include #include /* URL parsing function */ #include /* HTList object */ #include /* HTConfirm(), HTPrompt() */ #include /* AA common to both sides */ #include /* Assoc list */ #include /* Are we using an HTTP gateway? */ #include /* Implemented here */ #include /* Uuencoding and uudecoding */ #include /* * 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"); 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"); 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"); 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; } /***************** 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 */ len = (strlen(realm->realmname) + strlen(theHost ? theHost : "??") + 50); 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"); 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"); 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"); 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, int 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 (!(arg1 && *arg1 && args && *args)) { HTSprintf0(&temp, gettext("Invalid header '%s%s%s%s%s'"), line, (!isEmpty(arg1) ? " " : ""), (!isEmpty(arg1) ? arg1 : ""), (!isEmpty(args) ? " " : ""), (!isEmpty(args) ? 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; 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; }