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