1 /*
2  * $LynxId: HTAAUtil.c,v 1.35 2013/05/03 20:32:37 tom Exp $
3  *
4  * MODULE							HTAAUtil.c
5  *		COMMON PARTS OF ACCESS AUTHORIZATION MODULE
6  *			FOR BOTH SERVER AND BROWSER
7  *
8  * IMPORTANT:
9  *	Routines in this module use dynamic allocation, but free
10  *	automatically all the memory reserved by them.
11  *
12  *	Therefore the caller never has to (and never should)
13  *	free() any object returned by these functions.
14  *
15  *	Therefore also all the strings returned by this package
16  *	are only valid until the next call to the same function
17  *	is made.  This approach is selected, because of the nature
18  *	of access authorization: no string returned by the package
19  *	needs to be valid longer than until the next call.
20  *
21  *	This also makes it easy to plug the AA package in:
22  *	you don't have to ponder whether to free() something
23  *	here or is it done somewhere else (because it is always
24  *	done somewhere else).
25  *
26  *	The strings that the package needs to store are copied
27  *	so the original strings given as parameters to AA
28  *	functions may be freed or modified with no side effects.
29  *
30  *	The AA package does not free() anything else than what
31  *	it has itself allocated.
32  *
33  *	AA (Access Authorization) package means modules which
34  *	names start with HTAA.
35  *
36  * AUTHORS:
37  *	AL	Ari Luotonen	luotonen@dxcern.cern.ch
38  *	MD	Mark Donszelmann    duns@vxdeop.cern.ch
39  *
40  * HISTORY:
41  *	 8 Nov 93  MD	(VMS only) Added case insensitive comparison in HTAA_templateCaseMatch
42  *
43  *
44  * BUGS:
45  *
46  *
47  */
48 
49 #include <HTUtils.h>
50 
51 #include <HTAAUtil.h>		/* Implemented here     */
52 #include <HTAssoc.h>		/* Assoc list           */
53 #include <HTTCP.h>
54 #include <HTTP.h>
55 
56 #include <LYStrings.h>
57 #include <LYUtils.h>
58 #include <LYLeaks.h>
59 
60 /* PUBLIC						HTAAScheme_enum()
61  *		TRANSLATE SCHEME NAME INTO
62  *		A SCHEME ENUMERATION
63  *
64  * ON ENTRY:
65  *	name		is a string representing the scheme name.
66  *
67  * ON EXIT:
68  *	returns		the enumerated constant for that scheme.
69  */
HTAAScheme_enum(const char * name)70 HTAAScheme HTAAScheme_enum(const char *name)
71 {
72     char *upcased = NULL;
73 
74     if (!name)
75 	return HTAA_UNKNOWN;
76 
77     StrAllocCopy(upcased, name);
78     LYUpperCase(upcased);
79 
80     if (!StrNCmp(upcased, "NONE", 4)) {
81 	FREE(upcased);
82 	return HTAA_NONE;
83     } else if (!StrNCmp(upcased, "BASIC", 5)) {
84 	FREE(upcased);
85 	return HTAA_BASIC;
86     } else if (!StrNCmp(upcased, "PUBKEY", 6)) {
87 	FREE(upcased);
88 	return HTAA_PUBKEY;
89     } else if (!StrNCmp(upcased, "KERBEROSV4", 10)) {
90 	FREE(upcased);
91 	return HTAA_KERBEROS_V4;
92     } else if (!StrNCmp(upcased, "KERBEROSV5", 10)) {
93 	FREE(upcased);
94 	return HTAA_KERBEROS_V5;
95     } else {
96 	FREE(upcased);
97 	return HTAA_UNKNOWN;
98     }
99 }
100 
101 /* PUBLIC						HTAAScheme_name()
102  *			GET THE NAME OF A GIVEN SCHEME
103  * ON ENTRY:
104  *	scheme		is one of the scheme enum values:
105  *			HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
106  *
107  * ON EXIT:
108  *	returns		the name of the scheme, i.e.
109  *			"None", "Basic", "Pubkey", ...
110  */
HTAAScheme_name(HTAAScheme scheme)111 const char *HTAAScheme_name(HTAAScheme scheme)
112 {
113     switch (scheme) {
114     case HTAA_NONE:
115 	return "None";
116     case HTAA_BASIC:
117 	return "Basic";
118     case HTAA_PUBKEY:
119 	return "Pubkey";
120     case HTAA_KERBEROS_V4:
121 	return "KerberosV4";
122     case HTAA_KERBEROS_V5:
123 	return "KerberosV5";
124     case HTAA_UNKNOWN:
125 	return "UNKNOWN";
126     default:
127 	return "THIS-IS-A-BUG";
128     }
129 }
130 
131 /* PUBLIC						    HTAAMethod_enum()
132  *		TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
133  * ON ENTRY:
134  *	name		is the method name to translate.
135  *
136  * ON EXIT:
137  *	returns		HTAAMethod enumerated value corresponding
138  *			to the given name.
139  */
HTAAMethod_enum(const char * name)140 HTAAMethod HTAAMethod_enum(const char *name)
141 {
142     if (!name)
143 	return METHOD_UNKNOWN;
144 
145     if (0 == strcasecomp(name, "GET"))
146 	return METHOD_GET;
147     else if (0 == strcasecomp(name, "PUT"))
148 	return METHOD_PUT;
149     else
150 	return METHOD_UNKNOWN;
151 }
152 
153 /* PUBLIC						HTAAMethod_name()
154  *			GET THE NAME OF A GIVEN METHOD
155  * ON ENTRY:
156  *	method		is one of the method enum values:
157  *			METHOD_GET, METHOD_PUT, ...
158  *
159  * ON EXIT:
160  *	returns		the name of the scheme, i.e.
161  *			"GET", "PUT", ...
162  */
HTAAMethod_name(HTAAMethod method)163 const char *HTAAMethod_name(HTAAMethod method)
164 {
165     switch (method) {
166     case METHOD_GET:
167 	return "GET";
168     case METHOD_PUT:
169 	return "PUT";
170     case METHOD_UNKNOWN:
171 	return "UNKNOWN";
172     default:
173 	return "THIS-IS-A-BUG";
174     }
175 }
176 
177 /* PUBLIC						HTAAMethod_inList()
178  *		IS A METHOD IN A LIST OF METHOD NAMES
179  * ON ENTRY:
180  *	method		is the method to look for.
181  *	list		is a list of method names.
182  *
183  * ON EXIT:
184  *	returns		YES, if method was found.
185  *			NO, if not found.
186  */
HTAAMethod_inList(HTAAMethod method,HTList * list)187 BOOL HTAAMethod_inList(HTAAMethod method, HTList *list)
188 {
189     HTList *cur = list;
190     char *item;
191 
192     while (NULL != (item = (char *) HTList_nextObject(cur))) {
193 	CTRACE((tfp, " %s", item));
194 	if (method == HTAAMethod_enum(item))
195 	    return YES;
196     }
197 
198     return NO;			/* Not found */
199 }
200 
201 /* PUBLIC						HTAA_templateMatch()
202  *		STRING COMPARISON FUNCTION FOR FILE NAMES
203  *		   WITH ONE WILDCARD * IN THE TEMPLATE
204  * NOTE:
205  *	This is essentially the same code as in HTRules.c, but it
206  *	cannot be used because it is embedded in between other code.
207  *	(In fact, HTRules.c should use this routine, but then this
208  *	 routine would have to be more sophisticated... why is life
209  *	 sometimes so hard...)
210  *
211  * ON ENTRY:
212  *	ctemplate	is a template string to match the file name
213  *			against, may contain a single wildcard
214  *			character * which matches zero or more
215  *			arbitrary characters.
216  *	filename	is the filename (or pathname) to be matched
217  *			against the template.
218  *
219  * ON EXIT:
220  *	returns		YES, if filename matches the template.
221  *			NO, otherwise.
222  */
HTAA_templateMatch(const char * ctemplate,const char * filename)223 BOOL HTAA_templateMatch(const char *ctemplate,
224 			const char *filename)
225 {
226     const char *p = ctemplate;
227     const char *q = filename;
228     int m;
229 
230     for (; *p && *q && *p == *q; p++, q++)	/* Find first mismatch */
231 	;			/* do nothing else */
232 
233     if (!*p && !*q)
234 	return YES;		/* Equally long equal strings */
235     else if ('*' == *p) {	/* Wildcard */
236 	p++;			/* Skip wildcard character */
237 	m = (int) (strlen(q) - strlen(p));	/* Amount to match to wildcard */
238 	if (m < 0)
239 	    return NO;		/* No match, filename too short */
240 	else {			/* Skip the matched characters and compare */
241 	    if (strcmp(p, q + m))
242 		return NO;	/* Tail mismatch */
243 	    else
244 		return YES;	/* Tail match */
245 	}
246 	/* if wildcard */
247     } else
248 	return NO;		/* Length or character mismatch */
249 }
250 
251 /* PUBLIC						HTAA_templateCaseMatch()
252  *		STRING COMPARISON FUNCTION FOR FILE NAMES
253  *		   WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
254  * NOTE:
255  *	This is essentially the same code as in HTAA_templateMatch, but
256  *	it compares case insensitive (for VMS). Reason for this routine
257  *	is that HTAA_templateMatch gets called from several places, also
258  *	there where a case sensitive match is needed, so one cannot just
259  *	change the HTAA_templateMatch routine for VMS.
260  *
261  * ON ENTRY:
262  *	template	is a template string to match the file name
263  *			against, may contain a single wildcard
264  *			character * which matches zero or more
265  *			arbitrary characters.
266  *	filename	is the filename (or pathname) to be matched
267  *			against the template.
268  *
269  * ON EXIT:
270  *	returns		YES, if filename matches the template.
271  *			NO, otherwise.
272  */
HTAA_templateCaseMatch(const char * ctemplate,const char * filename)273 BOOL HTAA_templateCaseMatch(const char *ctemplate,
274 			    const char *filename)
275 {
276     const char *p = ctemplate;
277     const char *q = filename;
278     int m;
279 
280     /* Find first mismatch */
281     for (; *p && *q && TOUPPER(*p) == TOUPPER(*q); p++, q++) ;	/* do nothing else */
282 
283     if (!*p && !*q)
284 	return YES;		/* Equally long equal strings */
285     else if ('*' == *p) {	/* Wildcard */
286 	p++;			/* Skip wildcard character */
287 	m = (int) (strlen(q) - strlen(p));	/* Amount to match to wildcard */
288 	if (m < 0)
289 	    return NO;		/* No match, filename too short */
290 	else {			/* Skip the matched characters and compare */
291 	    if (strcasecomp(p, q + m))
292 		return NO;	/* Tail mismatch */
293 	    else
294 		return YES;	/* Tail match */
295 	}
296 	/* if wildcard */
297     } else
298 	return NO;		/* Length or character mismatch */
299 }
300 
301 /* PUBLIC					HTAA_makeProtectionTemplate()
302  *		CREATE A PROTECTION TEMPLATE FOR THE FILES
303  *		IN THE SAME DIRECTORY AS THE GIVEN FILE
304  *		(Used by server if there is no fancier way for
305  *		it to tell the client, and by browser if server
306  *		didn't send WWW-ProtectionTemplate: field)
307  * ON ENTRY:
308  *	docname is the document pathname (from URL).
309  *
310  * ON EXIT:
311  *	returns a template matching docname, and other files
312  *		files in that directory.
313  *
314  *		E.g.  /foo/bar/x.html  =>  /foo/bar/ *
315  *						    ^
316  *				Space only to prevent it from
317  *				being a comment marker here,
318  *				there really isn't any space.
319  */
HTAA_makeProtectionTemplate(const char * docname)320 char *HTAA_makeProtectionTemplate(const char *docname)
321 {
322     char *ctemplate = NULL;
323     char *slash = NULL;
324 
325     if (docname) {
326 	StrAllocCopy(ctemplate, docname);
327 	slash = strrchr(ctemplate, '/');
328 	if (slash)
329 	    slash++;
330 	else
331 	    slash = ctemplate;
332 	*slash = '\0';
333 	StrAllocCat(ctemplate, "*");
334     } else
335 	StrAllocCopy(ctemplate, "*");
336 
337     CTRACE((tfp, "make_template: made template `%s' for file `%s'\n",
338 	    ctemplate, docname));
339 
340     return ctemplate;
341 }
342 
343 /*
344  * Skip leading whitespace from *s forward
345  */
346 #define SKIPWS(s) while (*s==' ' || *s=='\t') s++;
347 
348 /*
349  * Kill trailing whitespace starting from *(s-1) backwards
350  */
351 #define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)='\0';}
352 
353 /* PUBLIC						HTAA_parseArgList()
354  *		PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
355  * ON ENTRY:
356  *	str	is a comma-separated list:
357  *
358  *			item, item, item
359  *		where
360  *			item ::= value
361  *			       | name=value
362  *			       | name="value"
363  *
364  *		Leading and trailing whitespace is ignored
365  *		everywhere except inside quotes, so the following
366  *		examples are equal:
367  *
368  *			name=value,foo=bar
369  *			 name="value",foo="bar"
370  *			  name = value ,  foo = bar
371  *			   name = "value" ,  foo = "bar"
372  *
373  * ON EXIT:
374  *	returns a list of name-value pairs (actually HTAssocList*).
375  *		For items with no name, just value, the name is
376  *		the number of order number of that item. E.g.
377  *		"1" for the first, etc.
378  */
HTAA_parseArgList(char * str)379 HTAssocList *HTAA_parseArgList(char *str)
380 {
381     HTAssocList *assoc_list = HTAssocList_new();
382     char *cur = NULL;
383     char *name = NULL;
384     int n = 0;
385 
386     if (!str)
387 	return assoc_list;
388 
389     while (*str) {
390 	SKIPWS(str);		/* Skip leading whitespace */
391 	cur = str;
392 	n++;
393 
394 	while (*cur && *cur != '=' && *cur != ',')
395 	    cur++;		/* Find end of name (or lonely value without a name) */
396 	KILLWS(cur);		/* Kill trailing whitespace */
397 
398 	if (*cur == '=') {	/* Name followed by a value */
399 	    *(cur++) = '\0';	/* Terminate name */
400 	    StrAllocCopy(name, str);
401 	    SKIPWS(cur);	/* Skip WS leading the value */
402 	    str = cur;
403 	    if (*str == '"') {	/* Quoted value */
404 		str++;
405 		cur = str;
406 		while (*cur && *cur != '"')
407 		    cur++;
408 		if (*cur == '"')
409 		    *(cur++) = '\0';	/* Terminate value */
410 		/* else it is lacking terminating quote */
411 		SKIPWS(cur);	/* Skip WS leading comma */
412 		if (*cur == ',')
413 		    cur++;	/* Skip separating colon */
414 	    } else {		/* Unquoted value */
415 		while (*cur && *cur != ',')
416 		    cur++;
417 		KILLWS(cur);	/* Kill trailing whitespace */
418 		if (*cur == ',')
419 		    *(cur++) = '\0';
420 		/* else *cur already NULL */
421 	    }
422 	} else {		/* No name, just a value */
423 	    if (*cur == ',')
424 		*(cur++) = '\0';	/* Terminate value */
425 	    /* else last value on line (already terminated by NULL) */
426 	    HTSprintf0(&name, "%d", n);		/* Item order number for name */
427 	}
428 	HTAssocList_add(assoc_list, name, str);
429 	str = cur;
430     }				/* while *str */
431 
432     FREE(name);
433     return assoc_list;
434 }
435 
436 /************** HEADER LINE READER -- DOES UNFOLDING *************************/
437 
438 #define BUFFER_SIZE	1024
439 
440 static size_t buffer_length;
441 static char *buffer = 0;
442 static char *start_pointer;
443 static char *end_pointer;
444 static int in_soc = -1;
445 
446 #ifdef LY_FIND_LEAKS
FreeHTAAUtil(void)447 static void FreeHTAAUtil(void)
448 {
449     FREE(buffer);
450 }
451 #endif /* LY_FIND_LEAKS */
452 
453 /* PUBLIC						HTAA_setupReader()
454  *		SET UP HEADER LINE READER, i.e., give
455  *		the already-read-but-not-yet-processed
456  *		buffer of text to be read before more
457  *		is read from the socket.
458  * ON ENTRY:
459  *	start_of_headers is a pointer to a buffer containing
460  *			the beginning of the header lines
461  *			(rest will be read from a socket).
462  *	length		is the number of valid characters in
463  *			'start_of_headers' buffer.
464  *	soc		is the socket to use when start_of_headers
465  *			buffer is used up.
466  * ON EXIT:
467  *	returns		nothing.
468  *			Subsequent calls to HTAA_getUnfoldedLine()
469  *			will use this buffer first and then
470  *			proceed to read from socket.
471  */
HTAA_setupReader(char * start_of_headers,size_t length,int soc)472 void HTAA_setupReader(char *start_of_headers,
473 		      size_t length,
474 		      int soc)
475 {
476     if (!start_of_headers)
477 	length = 0;		/* initialize length (is this reached at all?) */
478     if (buffer == NULL) {	/* first call? */
479 	buffer_length = length;
480 	if (buffer_length < BUFFER_SIZE)	/* would fall below BUFFER_SIZE? */
481 	    buffer_length = BUFFER_SIZE;
482 	buffer = (char *) malloc((size_t) (sizeof(char) * (buffer_length + 1)));
483     } else if (length > buffer_length) {	/* need more space? */
484 	buffer_length = length;
485 	buffer = (char *) realloc((char *) buffer,
486 				  (size_t) (sizeof(char) * (buffer_length + 1)));
487     }
488     if (buffer == NULL)
489 	outofmem(__FILE__, "HTAA_setupReader");
490 
491     assert(buffer != NULL);
492 
493 #ifdef LY_FIND_LEAKS
494     atexit(FreeHTAAUtil);
495 #endif
496     start_pointer = buffer;
497     if (start_of_headers) {
498 	LYStrNCpy(buffer, start_of_headers, length);
499 	end_pointer = buffer + length;
500     } else {
501 	*start_pointer = '\0';
502 	end_pointer = start_pointer;
503     }
504     in_soc = soc;
505 }
506 
507 /* PUBLIC						HTAA_getUnfoldedLine()
508  *		READ AN UNFOLDED HEADER LINE FROM SOCKET
509  * ON ENTRY:
510  *	HTAA_setupReader must absolutely be called before
511  *	this function to set up internal buffer.
512  *
513  * ON EXIT:
514  *	returns a newly-allocated character string representing
515  *		the read line.	The line is unfolded, i.e.
516  *		lines that begin with whitespace are appended
517  *		to current line.  E.g.
518  *
519  *			Field-Name: Blaa-Blaa
520  *			 This-Is-A-Continuation-Line
521  *			 Here-Is_Another
522  *
523  *		is seen by the caller as:
524  *
525  *	Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
526  *
527  */
HTAA_getUnfoldedLine(void)528 char *HTAA_getUnfoldedLine(void)
529 {
530     char *line = NULL;
531     char *cur;
532     int count;
533     BOOL peek_for_folding = NO;
534 
535     if (in_soc < 0) {
536 	CTRACE((tfp, "%s %s\n",
537 		"HTAA_getUnfoldedLine: buffer not initialized",
538 		"with function HTAA_setupReader()"));
539 	return NULL;
540     }
541 
542     for (;;) {
543 
544 	/* Reading from socket */
545 
546 	if (start_pointer >= end_pointer) {	/*Read the next block and continue */
547 #ifdef USE_SSL
548 	    if (SSL_handle)
549 		count = SSL_read(SSL_handle, buffer, BUFFER_SIZE);
550 	    else
551 		count = NETREAD(in_soc, buffer, BUFFER_SIZE);
552 #else
553 	    count = NETREAD(in_soc, buffer, BUFFER_SIZE);
554 #endif /* USE_SSL */
555 	    if (count <= 0) {
556 		in_soc = -1;
557 		return line;
558 	    }
559 	    if (count > (int) buffer_length)
560 		count = (int) buffer_length;
561 	    start_pointer = buffer;
562 	    end_pointer = buffer + count;
563 	    *end_pointer = '\0';
564 #ifdef NOT_ASCII
565 	    cur = start_pointer;
566 	    while (cur < end_pointer) {
567 		*cur = TOASCII(*cur);
568 		cur++;
569 	    }
570 #endif /*NOT_ASCII */
571 	}
572 	cur = start_pointer;
573 
574 	/* Unfolding */
575 
576 	if (peek_for_folding) {
577 	    if (*cur != ' ' && *cur != '\t')
578 		return line;	/* Ok, no continuation line */
579 	    else		/* So this is a continuation line, continue */
580 		peek_for_folding = NO;
581 	}
582 
583 	/* Finding end-of-line */
584 
585 	while (cur < end_pointer && *cur != '\n')	/* Find the end-of-line */
586 	    cur++;		/* (or end-of-buffer).  */
587 
588 	/* Terminating line */
589 
590 	if (cur < end_pointer) {	/* So *cur==LF, terminate line */
591 	    *cur = '\0';	/* Overwrite LF */
592 	    if (*(cur - 1) == '\r')
593 		*(cur - 1) = '\0';	/* Overwrite CR */
594 	    peek_for_folding = YES;	/* Check for a continuation line */
595 	}
596 
597 	/* Copying the result */
598 
599 	if (line)
600 	    StrAllocCat(line, start_pointer);	/* Append */
601 	else
602 	    StrAllocCopy(line, start_pointer);	/* A new line */
603 
604 	start_pointer = cur + 1;	/* Skip the read line */
605 
606     }				/* forever */
607 }
608