Annotation of libwww/Library/src/HTAAUtil.c, revision 2.20

2.9       frystyk     1: /*                                                                  HTAAUtil.c
                      2: **     COMMON PARTS OF ACCESS AUTHORIZATION MODULE
                      3: **     FOR BOTH SERVER AND BROWSER
                      4: **
2.13      frystyk     5: **     (c) COPYRIGHT MIT 1995.
2.9       frystyk     6: **     Please first read the full copyright statement in the file COPYRIGH.
2.1       luotonen    7: **
2.19      frystyk     8: **     The authentication information is stored in a list of authentication
                      9: **     data bases, each uniquely identified by a hostname and a port number.
                     10: **     Each data base contains a set of templates which can be used to predict
                     11: **     what information to use in a hierarchical tree. All authentication
                     12: **     dependent information is stored as opaque data in a anode. Normally
                     13: **     a server application would only keep one auth base but if it wants
                     14: **     different protection setup as a function of different interfaces then
                     15: **     it can have one auth base representing each interface. For example a
                     16: **     server with interfaces "www.foo.com" and "internal.foo.com" can have
                     17: **     different protection setups for each interface.
2.1       luotonen   18: **
                     19: ** AUTHORS:
                     20: **     AL      Ari Luotonen    luotonen@dxcern.cern.ch
2.3       duns       21: **     MD      Mark Donszelmann    duns@vxdeop.cern.ch
2.19      frystyk    22: **     HFN     Henrik Frystyk
2.1       luotonen   23: **
                     24: ** HISTORY:
2.4       luotonen   25: **      8 Nov 93  MD   (VMS only) Added case insensitive comparison
                     26: **                     in HTAA_templateCaseMatch
2.1       luotonen   27: */
                     28: 
2.11      frystyk    29: /* Library include files */
2.20    ! frystyk    30: #include "sysdep.h"
2.1       luotonen   31: #include "HTUtils.h"
2.11      frystyk    32: #include "HTString.h"
2.19      frystyk    33: #include "HTParse.h"
                     34: #include "HTReqMan.h"
                     35: #include "HTAssoc.h"
                     36: #include "HTAAUtil.h"                                   /* Implemented here */
                     37: 
                     38: typedef struct _HTAuthScheme {
                     39:     char *             scheme;
                     40:     HTAuthParCallback *        parser;
                     41:     HTAuthGenCallback *        generator;
                     42:     HTAuthGcCallback * gc;
                     43: } HTAuthScheme;
                     44: 
                     45: PRIVATE HTList * HTSchemes;    /* List of registered authentication schemes */
                     46: 
                     47: typedef struct _HTABase {                /* Server Authentication info base */
                     48:     char *     host;
                     49:     int                port;
                     50:     HTList *   templates;                /* List of templates for this base */
                     51:     HTList *   nodes;                        /* List of nodes for this base */
                     52: } HTABase;
                     53: 
                     54: typedef struct _HTANode {                /* Authentication scheme specifics */
                     55:     char *     realm;
                     56:     char *     scheme;
                     57:     void *     data;
                     58: } HTANode;
                     59: 
                     60: typedef struct _HTATemplate {                   /* Hierarchical information */
                     61:     char *     tmplate;
                     62:     HTANode *  node;
                     63: } HTATemplate;
                     64: 
                     65: PRIVATE HTList * AuthBases = NULL;           /* Current authentication base */
                     66: 
                     67: /* ------------------------------------------------------------------------- */
                     68: /*                          AUTHENTICATION SCHEMES                          */
                     69: /* ------------------------------------------------------------------------- */
                     70: 
                     71: /*     HTAuthCall_add
                     72: **     --------------
                     73: **     Register a callback functions that is to be called when we want to
                     74: **     parse challenges and to generate credentials - or the other way round.
                     75: **     If you are a server then you want to do the latter and if you are a 
                     76: **     client then you want to do the former.
                     77: */
2.20    ! frystyk    78: PUBLIC BOOL HTAuthCall_add (const char *       scheme,
2.19      frystyk    79:                            HTAuthParCallback * parser,
                     80:                            HTAuthGenCallback * generator,
                     81:                            HTAuthGcCallback *  gc)
                     82: {
                     83:     if (scheme && parser && generator && gc) {
                     84:        HTAuthScheme * me;
                     85:        if (AUTH_TRACE)
                     86:            HTTrace("Auth add.... %s with parser %p and generator %p\n",
                     87:                    scheme, (void *) parser, (void *) generator);
                     88:        if ((me = (HTAuthScheme *) HT_CALLOC(1, sizeof(HTAuthScheme))) == NULL)
                     89:            HT_OUTOFMEM("HTAuthCall_add");
                     90:        StrAllocCopy(me->scheme, scheme);
                     91:        me->parser = parser;
                     92:        me->generator = generator;
                     93:        me->gc = gc;
                     94:        if (!HTSchemes) HTSchemes = HTList_new();       
                     95:        return HTList_addObject(HTSchemes, (void *) me);
                     96:     }
                     97:     return NO;
                     98: }
2.1       luotonen   99: 
2.19      frystyk   100: /*     HTAuthCall_delete
                    101: **     -------------------
                    102: **     Unregister a authentication scheme from the list
                    103: **     Return YES if OK, else NO
                    104: */
2.20    ! frystyk   105: PUBLIC BOOL HTAuthCall_delete (const char * scheme)
2.19      frystyk   106: {
                    107:     HTList * cur = HTSchemes;
                    108:     if (scheme && cur) {
                    109:        HTAuthScheme * pres;
                    110:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
                    111:            if (!strcmp(scheme, pres->scheme)) {
                    112:                HTList_removeObject(HTSchemes, (void *) pres);
                    113:                HT_FREE(pres->scheme);
                    114:                HT_FREE(pres);
                    115:                return YES;
                    116:            }
                    117:        }
                    118:     }
                    119:     return NO;
                    120: }
2.1       luotonen  121: 
2.19      frystyk   122: /*     HTAuthCall_deleteAll
                    123: **     ----------------------
                    124: **     Unregisters all call back functions
                    125: **     Returns YES if OK, else NO
2.1       luotonen  126: */
2.19      frystyk   127: PUBLIC BOOL HTAuthCall_deleteAll (void)
2.1       luotonen  128: {
2.19      frystyk   129:     HTList * cur = HTSchemes;
                    130:     if (AUTH_TRACE) HTTrace("Auth delete. all schemes\n");
                    131:     if (cur) {
                    132:        HTAuthScheme * pres;
                    133:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
                    134:            HT_FREE(pres->scheme);
                    135:            HT_FREE(pres);
                    136:        }
                    137:        HTList_delete(HTSchemes);
                    138:        HTSchemes = NULL;
                    139:        return YES;
                    140:     }
                    141:     return NO;
                    142: }
2.1       luotonen  143: 
2.19      frystyk   144: /* ------------------------------------------------------------------------- */
                    145: /*                    AUTHENTICATION INFORMATION DATA BASE                  */
                    146: /* ------------------------------------------------------------------------- */
                    147: 
                    148: /*     This module maintains an authentication information database
                    149: **     which contains informations for generate either credentials or
                    150: **     challenges. The database is symmetric for both server and client
                    151: **     applications and the implementation can be changed independent of the 
2.20    ! frystyk   152: **     API so if you fell like using a fancy database toolkit then feel HT_FREE
2.19      frystyk   153: **     to go right ahead :-)
                    154: */
                    155: 
                    156: /*
                    157: **     Create a new anode
                    158: **     Returns new object or NULL if error
                    159: */
2.20    ! frystyk   160: PRIVATE HTANode * HTANode_new (HTABase * base, const char * realm,
        !           161:                               const char * scheme, void * data)
2.19      frystyk   162: {
                    163:     if (base && realm && scheme) {
                    164:        HTANode * me;
                    165:        if ((me = (HTANode *) HT_CALLOC(1, sizeof(HTANode))) == NULL)
                    166:            HT_OUTOFMEM("HTANode_new");
                    167:        StrAllocCopy(me->realm, realm);
                    168:        StrAllocCopy(me->scheme, scheme);
                    169:        me->data = data;
                    170:        HTList_addObject(base->nodes, (void *) me);
                    171:        return me;
                    172:     }
                    173:     return NULL;
                    174: }
2.1       luotonen  175: 
2.19      frystyk   176: /*
                    177: **     Delete a node. We call the scheme gc callback to handle the opaque
                    178: **     data object.
                    179: */
                    180: PRIVATE BOOL HTANode_delete (HTABase * base, HTANode * me)
                    181: {
                    182:     if (base && me) {
                    183:        HTAuth_cleanup(me->scheme, me->data);
                    184:        HT_FREE(me->realm);
                    185:        HT_FREE(me->scheme);
                    186:        HTList_removeObject(base->nodes, (void *) me);
                    187:        HT_FREE(me);
                    188:        return YES;
2.1       luotonen  189:     }
2.19      frystyk   190:     return NO;
2.1       luotonen  191: }
                    192: 
2.19      frystyk   193: /*
                    194: **     Search an authentication base for a matching anode.
                    195: **     Return the anode object found or NULL if none
                    196: */
2.20    ! frystyk   197: PRIVATE HTANode * HTANode_find (HTABase * base, const char * realm)
2.19      frystyk   198: {    
                    199:     if (base && base->nodes && realm) {
                    200:        HTList * cur = base->nodes;
                    201:        HTANode * pres;
                    202:        while ((pres = (HTANode *) HTList_nextObject(cur))) {
                    203:            if (!strcmp(pres->realm, realm)) return pres;
                    204:        }
                    205:     }
                    206:     return NULL;
                    207: }
2.1       luotonen  208: 
2.19      frystyk   209: /*
                    210: **     Create a new template and add to authentication base
                    211: **     Returns new object or NULL if error
                    212: */
                    213: PRIVATE HTATemplate * HTATemplate_new (HTABase * base, char * tmplate,
                    214:                                       HTANode * node)
                    215: {
                    216:     if (base && tmplate && node) {
                    217:        HTATemplate * me;
                    218:        if ((me = (HTATemplate *) HT_CALLOC(1, sizeof(HTATemplate))) == NULL)
                    219:            HT_OUTOFMEM("HTATemplate_new");
                    220:        me->node = node;
                    221:        me->tmplate = tmplate;
                    222:        HTList_addObject(base->templates, (void *) me);
                    223:        return me;
2.1       luotonen  224:     }
2.19      frystyk   225:     return NULL;
2.1       luotonen  226: }
                    227: 
2.19      frystyk   228: /*
                    229: **     Delete a template
                    230: */
                    231: PRIVATE BOOL HTATemplate_delete (HTABase * base, HTATemplate * me)
                    232: {
                    233:     if (base && me) {
                    234:        HT_FREE(me->tmplate);
                    235:        HTList_removeObject(base->templates, (void *) me);
                    236:        HT_FREE(me);
                    237:        return YES;
                    238:     }
                    239:     return NO;
                    240: }
2.1       luotonen  241: 
2.19      frystyk   242: /*
                    243: **     Search an authentication base for a matching template.
                    244: **     Return the template object found or NULL if none
                    245: */
2.20    ! frystyk   246: PRIVATE HTATemplate * HTATemplate_find (HTABase * base, const char *docname)
2.19      frystyk   247: {
                    248:     if (base && base->templates && docname) {
                    249:        HTList * cur = base->templates;
                    250:        HTATemplate * pres;
                    251:        while ((pres = (HTATemplate *) HTList_nextObject(cur))) {
                    252:            if (HTStrMatch(pres->tmplate, docname)) {
                    253:                if (AUTH_TRACE)
                    254:                    HTTrace("Template.... `%s' matched `%s'\n",
                    255:                            docname, pres->tmplate);
                    256:                return pres;
                    257:            }
                    258:        }
                    259:     }
                    260:     return NULL;
                    261: }
2.1       luotonen  262: 
2.19      frystyk   263: /*
                    264: **     Search an authentication base for a HTATemplate object in order to
                    265: **     update it with a new HTANode. We do this whenever we replace a anode
                    266: **     Return YES if OK, else NO
                    267: */
                    268: PRIVATE BOOL HTATemplate_update (HTABase * base, HTANode * old, HTANode *me)
                    269: {
                    270:     if (base && old && me) {
                    271:        HTList * cur = base->templates;
                    272:        HTATemplate * pres;
                    273:        while ((pres = (HTATemplate *) HTList_nextObject(cur))) {
                    274:            if (pres->node == old) {
                    275:                pres->node = me;
                    276:                if (AUTH_TRACE) HTTrace("Template.... updating %p\n", pres);
                    277:                return YES;
                    278:            }
                    279:        }
                    280:     }
                    281:     return NO;
                    282: }
2.1       luotonen  283: 
2.19      frystyk   284: /*
                    285: **     Create a new authentication base
                    286: **     Returns new object or NULL if error
                    287: */
2.20    ! frystyk   288: PRIVATE HTABase * HTABase_new (const char * host, int port)
2.19      frystyk   289: {
                    290:     if (host) {
                    291:        HTABase * me;
                    292:        if ((me = (HTABase *) HT_CALLOC(1, sizeof(HTABase))) == NULL)
                    293:            HT_OUTOFMEM("HTABase_new");
                    294:        StrAllocCopy(me->host, host);
                    295:        me->port = (port > 0 ? port : 80);
                    296:        me->templates = HTList_new();
                    297:        me->nodes = HTList_new();
                    298:        HTList_addObject(AuthBases, (void *) me);
                    299:        if (AUTH_TRACE) HTTrace("Auth Base... %p created\n", me);
                    300:        return me;
                    301:     }
                    302:     return NULL;
2.1       luotonen  303: }
                    304: 
2.19      frystyk   305: /*
                    306: **     Delete a complete server tree and everything within it.
                    307: */
                    308: PRIVATE BOOL HTABase_delete (HTABase * base)
                    309: {
                    310:     if (base) {
                    311:        HTList * cur;
                    312: 
                    313:        /* Free all templates */
                    314:        if ((cur = base->templates)) {
                    315:            HTATemplate * pres;
                    316:            while ((pres = (HTATemplate *) HTList_lastObject(cur)))
                    317:                HTATemplate_delete(base, pres);
                    318:            HTList_delete(base->templates);
                    319:        }
2.1       luotonen  320: 
2.19      frystyk   321:        /* Free all nodes */
                    322:        if ((cur = base->nodes)) {
                    323:            HTANode * pres;
                    324:            while ((pres = (HTANode *) HTList_lastObject(cur)))
                    325:                HTANode_delete(base, pres);
                    326:            HTList_delete(base->nodes);     
                    327:        }
2.1       luotonen  328: 
2.19      frystyk   329:        HT_FREE(base->host);
                    330:        HT_FREE(base);
                    331:        return YES;
                    332:     }
                    333:     return NO;
                    334: }
2.1       luotonen  335: 
                    336: /*
2.19      frystyk   337: **     Find a authentication base. Return NULL if not found
2.1       luotonen  338: */
2.20    ! frystyk   339: PRIVATE HTABase * HTABase_find (const char * host, int port)
2.19      frystyk   340: {
                    341:     HTList * cur = AuthBases;
                    342:     if (port <= 0) port = 80;
                    343:     if (host && cur) {
                    344:        HTABase * pres;
                    345:        while ((pres = (HTABase *) HTList_nextObject(cur))) {
                    346:            if (pres->port==port && !strcmp(pres->host, host)) return pres;
                    347:        }
                    348:     }
                    349:     return NULL;
                    350: }
2.1       luotonen  351: 
2.19      frystyk   352: /* ------------------------------------------------------------------------- */
                    353: /*                HANDLING THE AUTHENTICATION INFORMATION BASE              */
                    354: /* ------------------------------------------------------------------------- */
                    355: 
                    356: /*     HTAuthInfo_add
                    357: **     --------------
                    358: **     Add an access authentication information node to the database. If
                    359: **     the entry is already found then it is replaced with the new one.
                    360: **     The template must follow normal URI syntax but can include a wildcard
                    361: **     Return YES if added (or replaced), else NO
2.1       luotonen  362: */
2.20    ! frystyk   363: PUBLIC BOOL HTAuthInfo_add (const char * scheme, char * url,
2.19      frystyk   364:                            char * realm, void * data)
                    365: {
                    366:     HTABase * base;
                    367:     HTANode * anode;
                    368:     if (!scheme || !url || !realm || !data) return NO;
                    369:     if (AUTH_TRACE) HTTrace("Auth base... adding info for `%s'\n", url);
                    370:     if (!AuthBases) AuthBases = HTList_new();
                    371: 
                    372:     /* Find an existing authentication base or create new */
                    373:     {
                    374:        char * host = HTParse(url, "", PARSE_HOST);
                    375:        char * colon = strchr(host, ':');
                    376:        int port = 80;
                    377:        if (colon ) {
                    378:            *(colon++) = '\0';                       /* Chop off port number */
                    379:            port = atoi(colon);
                    380:        }
                    381:        if ((base = HTABase_find(host, port)) == NULL)
                    382:            base = HTABase_new(host, port);
                    383:        HT_FREE(host);
                    384:        if (!base) return NO;                  /* Couldn't create a new base */
                    385:     }
2.1       luotonen  386: 
2.19      frystyk   387:     /*
                    388:     ** Find a matching anode or create a new one. If we find an existing one
                    389:     ** we also update the template pointing to the anode. Otherwise we create
                    390:     ** a new template as well.
                    391:     */
                    392:     {
                    393:        HTANode * old = HTANode_find(base, realm);
                    394:        anode = HTANode_new(base, realm, scheme, data);
                    395:        if (old) {
                    396:            HTATemplate_update(base, old, anode);
                    397:            HTANode_delete(base, old);
                    398:        } else {
                    399:            char * docname = HTParse(url, "", PARSE_PATH);
                    400:            HTATemplate_new(base, docname, anode);
                    401:        }
                    402:     }
                    403:     return anode ? YES : NO;
                    404: }
                    405: 
                    406: /*     HTAuthInfo_deleteAll
                    407: **     --------------------
                    408: **     Remove the Database
                    409: */
                    410: PUBLIC BOOL HTAuthInfo_deleteAll (void)
                    411: {
                    412:     if (AuthBases) {
                    413:        HTList * cur = AuthBases;
                    414:        HTABase * pres;
                    415:        while ((pres = (HTABase *) HTList_nextObject(cur)))
                    416:            HTABase_delete(pres);
                    417:        HTList_delete(AuthBases);
                    418:        AuthBases = NULL;
                    419:        return YES;
                    420:     }
                    421:     return NO;
                    422: }
2.1       luotonen  423: 
2.19      frystyk   424: /*     AuthInfo_find
                    425: **     -------------
                    426: **     Seaches the set of authentication information bases for a match
                    427: **     In order to find an anode we do the following:
                    428: **
                    429: **             1) Find the right auth base
                    430: **             2) See if there is a realm match
                    431: **             3) See if there is a template match for URL
2.1       luotonen  432: **
2.19      frystyk   433: **     Return the node found else NULL which means that we don't have any
                    434: **     authentication information to hook on to this request or response
                    435: */
                    436: PRIVATE HTANode * HTAuthInfo_find (char * url, char * realm)
                    437: {
                    438:     HTABase * base;
                    439:     HTATemplate * tmplate;
                    440:     HTANode * anode;
                    441:     if (AUTH_TRACE)
                    442:        HTTrace("Auth base... looking for info on `%s'\n", url);
                    443: 
                    444:     /* Find an existing authentication base */
                    445:     {
                    446:        char * host = HTParse(url, "", PARSE_HOST);
                    447:        char * colon = strchr(host, ':');
                    448:        int port = 80;
                    449:        if (colon ) {
                    450:            *(colon++) = '\0';                       /* Chop off port number */
                    451:            port = atoi(colon);
                    452:        }       
                    453:        base = HTABase_find(host, port);
                    454:        HT_FREE(host);
                    455:        if (base == NULL) return NULL;                     /* Base not found */
                    456:     }
                    457: 
                    458:     /* Do we have a realm to look for? */
                    459:     if (realm) {
                    460:        if ((anode = HTANode_find(base, realm)) != NULL) {
                    461:            if (AUTH_TRACE)
                    462:                HTTrace("Auth info... found matching realm `%s\'\n", realm);
                    463:            return anode;
                    464:        }
                    465:     }
                    466: 
                    467:     /* If no realm or realm not found then look for template */
                    468:     {
                    469:        char * docname = HTParse(url, "", PARSE_PATH);
                    470:        if ((tmplate = HTATemplate_find(base, docname)) != NULL)
                    471:            anode = tmplate->node;
                    472:        HT_FREE(docname);
                    473:        return anode;
                    474:     }
                    475:     return NULL;                                                /* No match */
                    476: }
                    477: 
                    478: /* ------------------------------------------------------------------------- */
                    479: /*                PARSE AND GENERATE CHELLENGES AND CREDENTIALS             */
                    480: /* ------------------------------------------------------------------------- */
                    481: 
                    482: /*     HTAuth_parse
                    483: **     ------------
                    484: **     This function looks for a authentication scheme that matches what we
                    485: **     have in the request object and calls the parser callback function.
                    486: **     Case is not significant.
                    487: **     Return YES or whatever callback returns
                    488: */
                    489: PUBLIC BOOL HTAuth_parse (HTRequest * request)
                    490: {
                    491:     HTList * cur = HTSchemes;
                    492:     if (request && request->scheme && request->challenge && cur) {
                    493:        HTAuthScheme * pres;
                    494:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
                    495:            if (!strcasecomp(request->scheme, pres->scheme)) {
                    496:                if (AUTH_TRACE)
                    497:                    HTTrace("Auth Calling Parser %p\n", pres->parser);
                    498:                return (*(pres->parser))(request, pres->scheme);
2.1       luotonen  499:            }
2.19      frystyk   500:        }
                    501:     }
                    502:     if (AUTH_TRACE)HTTrace("Auth Parse.. No challenge or credentials found\n");
                    503:     return YES;
                    504: }
                    505: 
                    506: /*     HTAuth_generate
                    507: **     ---------------
                    508: **     This function looks for a any authentication scheme that protects 
                    509: **     this resource and calls the generator callback in order to make a
                    510: **     challenge or setup credentials depending on whether we are a server
                    511: **     or a client.
                    512: **     Return YES or whatever callback returns
                    513: */
                    514: PUBLIC BOOL HTAuth_generate (HTRequest * request)
                    515: {
                    516:     HTList * cur = HTSchemes;
                    517:     if (request && cur) {
                    518:        char * url = HTAnchor_physical(request->anchor);
                    519:        HTANode * node = HTAuthInfo_find(url, request->realm);
                    520:        if (node && node->data) {
                    521:            HTAuthScheme * pres;
                    522:            while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
                    523:                if (!strcasecomp(node->scheme, pres->scheme)) {
                    524:                    if (AUTH_TRACE)
                    525:                        HTTrace("Auth Calling Generator %p\n",pres->generator);
                    526:                    return (*(pres->generator))(request, node->scheme,
                    527:                                                node->realm, node->data);
                    528:                }
2.1       luotonen  529:            }
                    530:        }
2.19      frystyk   531:     }
                    532:     if (AUTH_TRACE)HTTrace("Auth Gen.... No challenge or credentials found\n");
                    533:     return YES;
2.1       luotonen  534: }
2.6       frystyk   535: 
2.19      frystyk   536: /*     HTAuth_cleanup
                    537: **     --------------
                    538: **     This function looks for a authentication scheme that matches what we
                    539: **     have in the request object and calls the cleanup callback function.
                    540: **     Case is not significant. If the scheme is not registered then
                    541: **     Return YES if callback found else NO
                    542: */
2.20    ! frystyk   543: PUBLIC BOOL HTAuth_cleanup (const char * scheme, void * data)
2.19      frystyk   544: {
                    545:     HTList * cur = HTSchemes;
                    546:     if (scheme && cur && data) {
                    547:        HTAuthScheme * pres;
                    548:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
                    549:            if (!strcasecomp(scheme, pres->scheme)) {
                    550:                if (AUTH_TRACE) HTTrace("Auth Calling gc %p\n", pres->gc);
                    551:                (*(pres->gc))(scheme, data);
                    552:                return YES;
                    553:            }
                    554:        }
                    555:     }
                    556:     return NO;
                    557: }
2.1       luotonen  558: 

Webmaster