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