/* MODULE HTAABrow.c ** BROWSER SIDE ACCESS AUTHORIZATION MODULE ** ** Containts 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: ** ** ** BUGS: ** ** */ #include /* strchr() */ #include "../config.h" #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 "HTAABrow.h" /* Implemented here */ #include "HTUU.h" /* Uuencoding and uudecoding */ #include "../libnut/str-tools.h" #include "../src/md5.h" /* MD5 code -- DXP */ /* defined in HTTP.c -- DXP */ extern int do_post; #ifndef DISABLE_TRACE extern int httpTrace; extern int www2Trace; #endif int securityType=HTAA_NONE; int securityDone=0; /* ** Local datatype definitions ** ** HTAAServer contains all the information about one server. */ typedef struct { char * hostname; /* Host's name */ int portnumber; /* Port number */ 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 * template; /* 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; /* ** Module-wide global variables */ PRIVATE HTList *server_table = NULL; /* Browser's info about servers */ PRIVATE char *secret_key = NULL; /* Browser's latest secret key */ PRIVATE HTAASetup *current_setup= NULL; /* The server setup we are currently */ /* talking to */ PRIVATE char *current_hostname = NULL; /* The server's name and portnumber */ PRIVATE int current_portnumber = 80; /* where we are currently trying to */ /* connect. */ PRIVATE char *current_docname = NULL; /* The document's name we are */ /* trying to access. */ PUBLIC void HTAARealm_clearall (HTList *realm_table); PUBLIC void HTAASetup_clearall (HTList *s); PUBLIC void HTAASetup_clearall (HTList *s) { HTList *n, *nn, *sn, *snn; /* HTAssocList **ss; */ HTAASetup *o; /* Walk the list of setups */ n = s; while (n) { nn = n->next; /* Free all of the object data */ o = (HTAASetup *)n->object; if (o) { if (o->template) free (o->template); sn = o->valid_schemes; while (sn) { snn = sn->next; free (sn); sn = snn; } } /* Should free the scheme_specifics stuff too */ /* Free the list structure and move on */ free (n); n = nn; } } /**************************** HTAAServer ***********************************/ /* PUBLIC HTAAServer_clearall() ** Clears password information for all servers ** On Entry: nothing ** On Exit: password information is gone. ** ** May 1996 PLB Created. ** */ PUBLIC void HTAAServer_clear () { HTList *n, *nn; HTAAServer *s; n = server_table; while (n) { nn = n->next; s = (HTAAServer *)n->object; if (s) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "Clearing passwd info for %s\n", s->hostname?s->hostname:"NULL"); #endif HTAARealm_clearall (s->realms); HTAASetup_clearall (s->setups); if (s->hostname) free (s->hostname); } free (n); n = nn; } server_table = NULL; secret_key = NULL; current_setup = NULL; current_hostname = NULL; current_docname = NULL; } /* PRIVATE 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. ** ** 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. */ PRIVATE HTAAServer *HTAAServer_new ARGS2(WWW_CONST char*, hostname, int, portnumber) { HTAAServer *server; if (!(server = (HTAAServer *)malloc(sizeof(HTAAServer)))) outofmem(__FILE__, "HTAAServer_new"); server->hostname = NULL; server->portnumber = (portnumber > 0 ? portnumber : 80); 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; } /* PRIVATE 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. */ /* PRIVATE HTAAServer_lookup() ** LOOK UP SERVER BY HOSTNAME AND PORTNUMBER ** ON ENTRY: ** hostname obvious. ** portnumber if non-positive defaults to 80. ** ** 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. */ PRIVATE HTAAServer *HTAAServer_lookup ARGS2(WWW_CONST char *, hostname, int, portnumber) { 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)) return server; } } return NULL; /* NULL parameter, or not found */ } /*************************** HTAASetup *******************************/ /* PRIVATE 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. ** ** 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. ** */ PRIVATE HTAASetup *HTAASetup_lookup ARGS3(WWW_CONST char *, hostname, int, portnumber, WWW_CONST char *, docname) { HTAAServer *server; HTAASetup *setup; if (portnumber <= 0) portnumber = 80; if (hostname && docname && *hostname && *docname && NULL != (server = HTAAServer_lookup(hostname, portnumber))) { HTList *cur = server->setups; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s (%s:%d:%s)\n", "HTAASetup_lookup: resolving setup for", hostname, portnumber, docname); #endif while (NULL != (setup = (HTAASetup*)HTList_nextObject(cur))) { if (HTAA_templateMatch(setup->template, docname)) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s `%s' %s `%s'\n", "HTAASetup_lookup:", docname, "matched template", setup->template); #endif return setup; } #ifndef DISABLE_TRACE else if (www2Trace) fprintf(stderr, "%s `%s' %s `%s'\n", "HTAASetup_lookup:", docname, "did NOT match template", setup->template); #endif } /* while setups remain */ } /* if valid parameters and server found */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s `%s' %s\n", "HTAASetup_lookup: No template matched", (docname ? docname : "(null)"), "(so probably not protected)"); #endif return NULL; /* NULL in parameters, or not found */ } /* PRIVATE HTAASetup_new() ** CREATE A NEW SETUP NODE ** ON ENTRY: ** server is a pointer to a HTAAServer structure ** to which this setup belongs. ** template 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. */ PRIVATE HTAASetup *HTAASetup_new ARGS4(HTAAServer *, server, char *, template, HTList *, valid_schemes, HTAssocList **, scheme_specifics) { HTAASetup *setup; if (!server || !template || !*template) return NULL; if (!(setup = (HTAASetup*)malloc(sizeof(HTAASetup)))) outofmem(__FILE__, "HTAASetup_new"); setup->retry = NO; setup->server = server; setup->template = NULL; if (template) StrAllocCopy(setup->template, template); setup->valid_schemes = valid_schemes; setup->scheme_specifics = scheme_specifics; HTList_addObject(server->setups, (void*)setup); return setup; } /* PRIVATE HTAASetup_delete() ** FREE A HTAASetup STRUCTURE ** ON ENTRY: ** killme is a pointer to the structure to free(). ** ** ON EXIT: ** returns nothing. */ /* PRIVATE 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. */ PRIVATE void HTAASetup_updateSpecifics ARGS2(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 **********************************/ /* PUBLIC HTAARealm_clearall() ** Clears all realm information. ** On Entry: ** realm_table a list of realm objects ** ** On Exit: ** returns: Nothing. realm_table is no longer valid. */ PUBLIC void HTAARealm_clearall ARGS1(HTList *, realm_table) { HTList *n, *nn; HTAARealm *r; n = realm_table; while (n) { nn = n->next; r = (HTAARealm *)n->object; if (r) { #ifdef PAUL_IS_A_GIMP if (www2Trace) fprintf(stderr, "Clearing %s %s:%s\n", r->realmname?r->realmname:"NULL", r->realmname?r->username:"NULL", r->realmname?r->password:"NULL"); #endif if (r->realmname) free (r->realmname); if (r->username) free (r->username); if (r->password) free (r->password); } free (n->object); free (n); n = nn; } } /* PRIVATE 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. */ PRIVATE HTAARealm *HTAARealm_lookup ARGS2(HTList *, realm_table, WWW_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 */ } /* PRIVATE 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. */ PRIVATE HTAARealm *HTAARealm_new ARGS4(HTList *, realm_table, WWW_CONST char *, realmname, WWW_CONST char *, username, WWW_CONST char *, password) { HTAARealm *realm; realm = HTAARealm_lookup(realm_table, realmname); if (!realm) { if (!(realm = (HTAARealm*)malloc(sizeof(HTAARealm)))) 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; } /* BROWSER PRIVATE 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. */ PRIVATE HTAAScheme HTAA_selectScheme ARGS1(HTAASetup *, setup) { HTAAScheme scheme; if (setup && setup->valid_schemes) { for (scheme=HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++) if (-1 < HTList_indexOf(setup->valid_schemes, (void*)scheme)) return scheme; } return HTAA_BASIC; } /***************** Basic and Pubkey Authentication ************************/ /* PRIVATE 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. ** realmname is the password domain name. ** ** 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). ** NOTE: ** Like throughout the entire AA package, no string or structure ** returned by AA package needs to (or should) be freed. ** */ PRIVATE char *compose_auth_string ARGS2(HTAAScheme, scheme, HTAASetup *, setup) { static char *result = NULL; /* Uuencoded presentation, the result */ char *cleartext = NULL; /* Cleartext presentation */ char *ciphertext = NULL; /* Encrypted presentation */ int len; char *username; char *password; char *realmname; HTAARealm *realm; char *inet_addr = "0.0.0.0"; /* Change... @@@@ */ char *timestamp = "42"; /* ... these @@@@ */ /* for MD5 -- DXP */ char * nonce; /* Server specified integer value */ char * opaque; /* more random MD5 junk... */ BOOL stale; /* flag indicating the previous request from the client was rejected because the nonce value was stale */ FREE(result); /* From previous call */ if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY && scheme != HTAA_MD5) || !setup || !setup->scheme_specifics || !setup->scheme_specifics[scheme] || !setup->server || !setup->server->realms) return ""; realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm"); if (!realmname) return ""; realm = HTAARealm_lookup(setup->server->realms, realmname); if (!realm || setup->retry) { char msg[2048]; if (!realm) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s `%s' %s\n", "compose_auth_string: realm:", realmname, "not found -- creating"); #endif realm = HTAARealm_new(setup->server->realms, realmname, NULL,NULL); sprintf(msg, "Document is protected.\nEnter username for %s at %s: ", realm->realmname, setup->server->hostname ? setup->server->hostname : "??"); realm->username = HTPrompt(msg, realm->username); /* Added by marca. */ if (!realm->username) return ""; } else { sprintf(msg,"Enter username for %s at %s: ", realm->realmname, setup->server->hostname ? setup->server->hostname : "??"); username = HTPrompt(msg, realm->username); FREE(realm->username); realm->username = username; /* Added by marca. */ if (!realm->username) return ""; } password = HTPromptPassword("Enter password to authenticate yourself: "); FREE(realm->password); realm->password = password; /* Added by marca. */ if (!realm->password) return ""; } len = strlen(realm->username ? realm->username : "") + strlen(realm->password ? realm->password : "") + 3; if (scheme == HTAA_PUBKEY) { #ifdef PUBKEY /* Generate new secret key */ StrAllocCopy(secret_key, HTAA_generateRandomKey()); #endif /* Room for secret key, timestamp and inet address */ len += strlen(secret_key ? secret_key : "") + 30; } else FREE(secret_key); if (!(cleartext = (char*)calloc(len, 1))) outofmem(__FILE__, "compose_auth_string"); if (realm->username) strcpy(cleartext, realm->username); else *cleartext = (char)0; strcat(cleartext, ":"); if (realm->password) strcat(cleartext, realm->password); if (scheme == HTAA_PUBKEY) { strcat(cleartext, ":"); strcat(cleartext, inet_addr); strcat(cleartext, ":"); strcat(cleartext, timestamp); strcat(cleartext, ":"); if (secret_key) strcat(cleartext, secret_key); if (!((ciphertext = (char*)malloc(2*len)) && (result = (char*)malloc(3*len)))) outofmem(__FILE__, "compose_auth_string"); #ifdef PUBKEY HTPK_encrypt(cleartext, ciphertext, server->public_key); /* marca added unsigned char * cast for HP. */ HTUU_encode((unsigned char *)ciphertext, strlen(ciphertext), result); #endif free(cleartext); free(ciphertext); } /* the following added by DXP */ else if(scheme == HTAA_MD5) { unsigned char *md5_cleartext = NULL; /* Cleartext presentation */ unsigned char *md5_ciphertext = NULL; /* Encrypted presentation */ unsigned char *A1, *A2; unsigned char *digest1, *digest2; unsigned char *hex1, *hex2; if (!(result = (char*)malloc(300))) outofmem(__FILE__, "compose_auth_string"); nonce = HTAssocList_lookup(setup->scheme_specifics[scheme], "nonce"); if (!nonce) return ""; opaque = HTAssocList_lookup(setup->scheme_specifics[scheme], "opaque"); if (!opaque) return ""; if (!(A1 = (unsigned char*)malloc(strlen(realm->username) + strlen(realm->realmname) + strlen(realm->password) + 3 + 1))) outofmem(__FILE__, "compose_auth_string"); if (!(A2 = (unsigned char*)malloc(4 + strlen(current_docname) + 1 + 1 ))) outofmem(__FILE__, "compose_auth_string"); /* make A1 */ *A1 = (unsigned char)0; strcat(A1, realm->username); strcat(A1, ":"); strcat(A1, realm->realmname); strcat(A1, ":"); strcat(A1, realm->password); strcat(A1, "\0"); /* make A2 */ *A2 = (unsigned char)0; if (do_post) strcat(A2, "POST"); else strcat(A2, "GET"); strcat(A2, ":"); strcat(A2, current_docname); strcat(A2, "\0"); if (!(md5_cleartext = (unsigned char*)malloc(100 + 1))) outofmem(__FILE__, "compose_auth_string"); if (!(md5_ciphertext = (unsigned char*)malloc(100 + 1))) outofmem(__FILE__, "compose_auth_string"); if (!(hex1 = (unsigned char*)malloc(32 + 1))) outofmem(__FILE__, "compose_auth_string"); if (!(hex2 = (unsigned char*)malloc(32 + 1))) outofmem(__FILE__, "compose_auth_string"); if (!(digest1 = (unsigned char*)malloc(16))) outofmem(__FILE__, "compose_auth_string"); if (!(digest2 = (unsigned char*)malloc(16))) outofmem(__FILE__, "compose_auth_string"); MD5Mem(A1, strlen(A1), digest1); MD5Mem(A2, strlen(A2), digest2); MD5Convert_to_Hex(digest1, hex1); MD5Convert_to_Hex(digest2, hex2); /* make md5_cleartext */ *md5_cleartext = (unsigned char)0; strcat(md5_cleartext, hex1); strcat(md5_cleartext, ":"); strcat(md5_cleartext, nonce); strcat(md5_cleartext, ":"); strcat(md5_cleartext, hex2); MD5Mem(md5_cleartext, strlen(md5_cleartext), digest1); MD5Convert_to_Hex(digest1, md5_ciphertext); *result = (char)0; strcat(result, "username=\""); strcat(result, realm->username); strcat(result, "\", realm=\""); strcat(result, realm->realmname); strcat(result, "\", nonce=\""); strcat(result, nonce); strcat(result, "\", uri=\""); strcat(result, current_docname); strcat(result, "\", response=\""); strcat(result, md5_ciphertext); strcat(result, "\", opaque=\""); strcat(result, opaque); strcat(result, "\""); /* since all we need from here on out is the result, get rid of all the rest */ free(A1); free(A2); free(digest1); free(digest2); free(hex1); free(hex2); free(md5_cleartext); free(md5_ciphertext); } else { /* scheme == HTAA_BASIC */ /* Added "+ 1" marca. */ #ifdef OLD if (!(result = (char*)malloc(len + len/2 + 1))) #else /* Ari fix. */ if (!(result = (char*)malloc(4 * ((len+2)/3) + 1))) #endif outofmem(__FILE__, "compose_auth_string"); /* Added cast to unsigned char * on advice of erik@sockdev.uni-c.dk (Erik Bertelsen). */ HTUU_encode((unsigned char *)cleartext, strlen(cleartext), result); free(cleartext); } #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"sending auth line: %s\n",result); #endif return result; } /* 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) ** ** 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. */ PUBLIC char *HTAA_composeAuth ARGS3(WWW_CONST char *, hostname, WWW_CONST int, portnumber, WWW_CONST char *, docname) { static char *result = NULL; char *auth_string; #if defined(KRB4) || defined(KRB5) char *compose_kerberos_auth_string(HTAAScheme, char *); #endif BOOL retry; HTAAScheme scheme; FREE(result); /* From previous call */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "Composing Authorization for %s:%d/%s\n", hostname, portnumber, docname); #endif 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); if (!current_setup) return NULL; switch (scheme = HTAA_selectScheme(current_setup)) { case HTAA_BASIC: case HTAA_PUBKEY: case HTAA_MD5: /* DXP */ auth_string = compose_auth_string(scheme, current_setup); break; #ifdef KRB4 case HTAA_KERBEROS_V4: auth_string = compose_kerberos_auth_string(scheme, hostname); break; #endif #ifdef KRB5 case HTAA_KERBEROS_V5: auth_string = compose_kerberos_auth_string(scheme, hostname); break; #endif /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */ default: { char msg[2048]; sprintf(msg, "%s %s `%s'", "This client doesn't know how to compose authentication", "information for scheme", HTAAScheme_name(scheme)); HTAlert(msg); auth_string = NULL; } } /* switch scheme */ if (!securityDone) { if (!auth_string || !*auth_string) { securityType=HTAA_NONE; } else { securityType=scheme; } } else { securityDone=0; } current_setup->retry = NO; /* Added by marca. */ if (!auth_string) return NULL; if (!(result = (char*)malloc(sizeof(char) * (strlen(auth_string)+40)))) outofmem(__FILE__, "HTAA_composeAuth"); strcpy(result, "Authorization: "); strcat(result, HTAAScheme_name(scheme)); strcat(result, " "); strcat(result, auth_string); return result; } /* 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. ** ** 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. */ PUBLIC BOOL HTAA_shouldRetryWithAuth ARGS3(char *, start_of_headers, int, length, int, soc) { HTAAScheme scheme; char *line; int num_schemes = 0; HTList *valid_schemes = HTList_new(); HTAssocList **scheme_specifics = NULL; char *template = NULL; /* Read server reply header lines */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "Server reply header lines:\n"); #endif HTAA_setupReader(start_of_headers, length, soc); while (NULL != (line = HTAA_getUnfoldedLine()) && *line != (char)0) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s\n", line); #endif if (strchr(line, ':')) { /* Valid header line */ char *p = line; char *fieldname = HTNextField(&p); char *arg1 = HTNextField(&p); char *args = p; if (0==my_strcasecmp(fieldname, "WWW-Authenticate:")) { if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) { HTList_addObject(valid_schemes, (void*)scheme); if (!scheme_specifics) { int i; scheme_specifics = (HTAssocList**) malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*)); 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++; } #ifndef DISABLE_TRACE else if (www2Trace) { fprintf(stderr, "Unknown scheme `%s' %s\n", (arg1 ? arg1 : "(null)"), "in WWW-Authenticate: field"); } #endif } else if (0==my_strcasecmp(fieldname, "WWW-Protection-Template:")) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "Protection template set to `%s'\n", arg1); #endif StrAllocCopy(template, arg1); } } /* if a valid header line */ #ifndef DISABLE_TRACE else if (www2Trace) { fprintf(stderr, "Invalid header line `%s' ignored\n", line); } /* else invalid header line */ #endif } /* while header lines remain */ /* So should we retry with authorization */ if (num_schemes == 0) { /* No authentication valid */ current_setup = NULL; return NO; } if (current_setup && current_setup->server) { /* So we have already tried with 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("Authorization failed. Retry?")) { current_setup = NULL; return NO; } /* HTConfirm(...) == NO */ else { /* re-ask username+password (if misspelled) */ current_setup->retry = YES; return YES; } /* HTConfirm(...) == YES */ } /* if current_setup != NULL */ 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); if (!server) { server = HTAAServer_new(current_hostname, current_portnumber); } if (!template) template = HTAA_makeProtectionTemplate(current_docname); current_setup = HTAASetup_new(server, template, valid_schemes, scheme_specifics); HTAlert("Access without authorization denied -- retrying"); return YES; } /* else current_setup == NULL */ /* Never reached */ }