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