/* ** COMMON PARTS OF ACCESS AUTHORIZATION MODULE ** FOR BOTH SERVER AND BROWSER ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTAAUtil.c,v 2.30 1998/05/04 19:36:06 frystyk Exp $ ** ** The authentication information is stored in a list of authentication ** data bases, each uniquely identified by a hostname and a port number. ** Each data base contains a set of templates which can be used to predict ** what information to use in a hierarchical tree. All authentication ** dependent information is stored as opaque data in a anode. Normally ** a server application would only keep one auth base but if it wants ** different protection setup as a function of different interfaces then ** it can have one auth base representing each interface. For example a ** server with interfaces "www.foo.com" and "internal.foo.com" can have ** different protection setups for each interface. ** ** AUTHORS: ** AL Ari Luotonen luotonen@dxcern.cern.ch ** MD Mark Donszelmann duns@vxdeop.cern.ch ** HFN Henrik Frystyk ** ** HISTORY: ** 8 Nov 93 MD (VMS only) Added case insensitive comparison ** in HTAA_templateCaseMatch */ /* Library include files */ #include "wwwsys.h" #include "WWWUtil.h" #include "WWWCore.h" #include "HTAAUtil.h" /* Implemented here */ #define AA_TREE "w3c-AA" /* Name of the AA tree */ #define AA_PROXY_TREE "w3c-proxy-AA" /* Name of the proxy AA tree */ #define DEFAULT_PORT 80 /* Concentrate on HTTP */ struct _HTAAModule { char * scheme; HTNetBefore * before; HTNetAfter * after; HTUTree_gc * gc; }; typedef struct _HTAAElement { char * scheme; void * context; } HTAAElement; PRIVATE HTList * HTSchemes; /* List of registered authentication schemes */ /* ------------------------------------------------------------------------- */ /* AUTHENTICATION MODULE MANAGEMENT */ /* ------------------------------------------------------------------------- */ PRIVATE BOOL delete_module (HTAAModule * module) { if (module) { HT_FREE(module->scheme); HT_FREE(module); return YES; } return NO; } PRIVATE HTAAModule * find_module (const char * scheme) { if (!HTSchemes) HTSchemes = HTList_new(); if (scheme) { HTList * cur = HTSchemes; HTAAModule * pres = NULL; while ((pres = (HTAAModule *) HTList_nextObject(cur))) if (!strcasecomp(pres->scheme, scheme)) return pres; } else if (AUTH_TRACE) HTTrace("Auth Engine. Bad argument\n"); return NULL; } PUBLIC HTAAModule * HTAA_newModule (const char * scheme, HTNetBefore * before, HTNetAfter * after, HTUTree_gc * gc) { if (scheme) { HTAAModule * pres = find_module(scheme); /* If found then update entry - else create a new one */ if (!pres) { if (!(pres = (HTAAModule *) HT_CALLOC(1, sizeof(HTAAModule)))) HT_OUTOFMEM("HTAA_newModule"); StrAllocCopy(pres->scheme, scheme); pres->before = before; pres->after = after; pres->gc = gc; /* Add the new AA Module to the list */ HTList_addObject(HTSchemes, (void *) pres); if (AUTH_TRACE) HTTrace("Auth Engine. Created module %p\n", pres); } else { if (AUTH_TRACE) HTTrace("Auth Engine. Found module %p\n", pres); } return pres; } else { if (AUTH_TRACE) HTTrace("Auth Engine. Bad argument\n"); return NULL; } } PUBLIC HTAAModule * HTAA_findModule (const char * scheme) { if (scheme) { HTAAModule * pres = find_module(scheme); if (AUTH_TRACE) HTTrace("Auth Engine. did %sfind %s\n", pres ? "" : "NOT ",scheme); return pres; } else { if (AUTH_TRACE) HTTrace("Auth Engine. Bad augument\n"); } return NULL; } PUBLIC BOOL HTAA_deleteModule (const char * scheme) { if (scheme) { HTAAModule * pres = find_module(scheme); if (pres) { HTList_removeObject(HTSchemes, pres); if (AUTH_TRACE) HTTrace("Auth Engine. deleted %p\n", pres); delete_module(pres); return YES; } } return NO; } PUBLIC BOOL HTAA_deleteAllModules (void) { if (HTSchemes) { HTList * cur = HTSchemes; HTAAModule * pres; while ((pres = (HTAAModule *) HTList_nextObject(cur))) delete_module(pres); HTList_delete(HTSchemes); HTSchemes = NULL; return YES; } return NO; } /* ------------------------------------------------------------------------- */ /* HANDLE THE AA URL TREE */ /* ------------------------------------------------------------------------- */ /* ** A AA element is a particular AA procotol associated with a ** particular point in the URL tree. The scheme is the name of the ** AA protocol known to be able to handle this context. This protocol ** must have been registered as a AA module. */ PRIVATE HTAAElement * HTAA_newElement (const char * scheme, void * context) { if (scheme) { HTAAElement * me; if ((me = (HTAAElement *) HT_CALLOC(1, sizeof(HTAAElement))) == NULL) HT_OUTOFMEM("HTAAElement_new"); StrAllocCopy(me->scheme, scheme); me->context = context; if (AUTH_TRACE) HTTrace("Auth Engine. Created element %p\n", me); return me; } return NULL; } /* ** If the new context differs from the existing one then use the ** new one, otherwise only override the old context if new ** one differs from NULL */ PRIVATE BOOL HTAA_updateElement (HTAAElement * element, const char * scheme, void * context) { if (element && scheme) { /* ** If the old context differs from the new one then ** call the gc provided by the caller */ if (context && context != element->context) { HTAAModule * module = HTAA_findModule(element->scheme); if (module && module->gc && element->context) (*module->gc)(element->context); /* ** Insert the new scheme */ StrAllocCopy(element->scheme, scheme); element->context = context; } return YES; } return NO; } PRIVATE int HTAA_deleteElement (void * context) { HTAAElement * me = (HTAAElement *) context; if (me) { HTAAModule * module = HTAA_findModule(me->scheme); /* If module then call the gc of the Authentication Module */ if (module && module->gc && me->context) (*module->gc)(me->context); if (AUTH_TRACE) HTTrace("Auth Engine. Deleted element %p\n", me); HT_FREE(me->scheme); HT_FREE(me); return YES; } return NO; } /* ** Find AA Element ** --------------- ** Seaches the set of authentication information bases for a match ** In order to find an anode we do the following: ** ** 1) Find the right auth base ** 2) See if there is a realm match ** 3) See if there is a template match for URL ** ** Return the node found else NULL which means that we don't have any ** authentication information to hook on to this request or response */ PRIVATE HTAAElement * HTAA_findElement (BOOL proxy_access, const char * realm, const char * url) { HTUTree * tree; if (!url) { if (AUTH_TRACE) HTTrace("Auth Engine. Bad argument\n"); return NULL; } if (AUTH_TRACE) HTTrace("Auth Engine. Looking up `%s'\n", url); /* Find an existing URL Tree for this URL (if any) */ { char * host = HTParse(url, "", PARSE_HOST); char * colon = strchr(host, ':'); int port = DEFAULT_PORT; if (colon ) { *(colon++) = '\0'; /* Chop off port number */ port = atoi(colon); } tree = HTUTree_find(proxy_access ? AA_PROXY_TREE : AA_TREE, host,port); HT_FREE(host); if (!tree) { if (AUTH_TRACE) HTTrace("Auth Engine. No information\n"); return NULL; } } /* Find a matching AA element (if any) */ { char * path = HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION); HTAAElement *element = (HTAAElement*)HTUTree_findNode(tree,realm,path); HT_FREE(path); return element; } return NULL; } /* Add a AA context to the URL tree ** -------------------------------- ** Each node in the AA URL tree is a list of the modules we must call ** for this particular node. */ PUBLIC void * HTAA_updateNode (BOOL proxy_access, char const * scheme, const char * realm, const char * url, void * context) { HTUTree * tree = NULL; HTAAModule * module = NULL; if (!scheme || !url) { if (AUTH_TRACE) HTTrace("Auth Engine. Bad argument\n"); return NULL; } if (AUTH_TRACE) HTTrace("Auth Engine. Adding info for `%s'\n", url); /* Find the AA module with this name */ if ((module = HTAA_findModule(scheme)) == NULL) { if (AUTH_TRACE) HTTrace("Auth Engine. Module `%s\' not registered\n", scheme ? scheme : ""); return NULL; } /* Find an existing URL Tree or create a new one */ { char * host = HTParse(url, "", PARSE_HOST); char * colon = strchr(host, ':'); int port = DEFAULT_PORT; if (colon ) { *(colon++) = '\0'; /* Chop off port number */ port = atoi(colon); } tree = HTUTree_new(proxy_access ? AA_PROXY_TREE : AA_TREE, host, port, HTAA_deleteElement); HT_FREE(host); if (!tree) { if (AUTH_TRACE) HTTrace("Auth Engine. Can't create tree\n"); return NULL; } } /* Find a matching AA element or create a new one */ { char * path = HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION); HTAAElement * element = NULL; BOOL status; if ((element = (HTAAElement *) HTUTree_findNode(tree, realm, path))) status = HTAA_updateElement(element, scheme, context); else { element = HTAA_newElement(scheme, context); status = HTUTree_addNode(tree, realm, path, element); } HT_FREE(path); return status==YES ? element->context : NULL; } } /* ------------------------------------------------------------------------- */ /* AUTHENTICATION ENGINE */ /* ------------------------------------------------------------------------- */ /* HTAA_beforeFilter ** ------------------ ** Make a lookup in the URL tree to find any context for this node, ** If no context is found then we assume that we don't know anything about ** this URL and hence we don't call any BEFORE filters at all. ** Return HT_OK or whatever callback returns */ PUBLIC int HTAA_beforeFilter (HTRequest * request, void * param, int mode) { char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request)); const char * realm = HTRequest_realm(request); HTAAElement * element = HTAA_findElement(NO, realm, url); HT_FREE(url); /* If we have an element then call the before filter with this scheme */ if (element) { HTAAModule * module = HTAA_findModule(element->scheme); if (module) { if (AUTH_TRACE) HTTrace("Auth Engine. Found BEFORE filter %p\n", module->before); return (*module->before)(request, element->context, mode); } } return HT_OK; } /* HTAA_afterFilter ** ----------------- ** Call the AFTER filter that knows how to handle this scheme. ** Return YES or whatever callback returns */ PUBLIC int HTAA_afterFilter (HTRequest * request, HTResponse * response, void * param, int status) { const char * scheme = HTResponse_scheme(response); HTAAModule * module = NULL; if (AUTH_TRACE) HTTrace("Auth Engine. After filter status %d\n", status); /* ** If we don't have a scheme then the server has made an error. We ** try to make up for it by creating our own "noop" realm and use basic. */ if (!scheme) { HTResponse_addChallenge(response, "basic", "realm LIBWWW-UNKNOWN"); scheme = "basic"; } if ((module = HTAA_findModule(scheme)) != NULL) { if (AUTH_TRACE) HTTrace("Auth Engine. Found AFTER filter %p\n", module->after); HTRequest_deleteCredentialsAll(request); return (*module->after)(request, response, NULL, status); } return HT_ERROR; } /* HTAA_proxybeforeFilter ** ---------------------- ** Make a lookup in the proxy URL tree to find any context for this node, ** If no context is found then we assume that we don't know anything about ** this URL and hence we don't call any BEFORE filters at all. ** Return HT_OK or whatever callback returns */ PUBLIC int HTAA_proxyBeforeFilter (HTRequest * request, void * param, int mode) { char * url = HTRequest_proxy(request); /* ** We may not have a proxy - for example if it has been disabled for this ** request or it isn't a proxied access method. */ if (url) { const char * realm = HTRequest_realm(request); HTAAElement * element = HTAA_findElement(YES, realm, url); /* If we have an element then call the before filter with the scheme */ if (element) { HTAAModule * module = HTAA_findModule(element->scheme); if (module) { if (AUTH_TRACE) HTTrace("Auth Engine. Found Proxy BEFORE filter %p with context %p\n", module->before, element->context); return (*module->before)(request, element->context, HT_NO_PROXY_ACCESS); } } } return HT_OK; }