+Missing <time.h> include
[middleman.git] / src / filter.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include "proto.h"
23
24 /*
25 load <filter> section of xml_list into a FILTER_LIST structure
26 */
27 FILTER_LIST *filter_load(FILTER_LIST * filter_list, XML_LIST * xml_list)
28 {
29         FILTER_LIST *tmp_list = filter_list;
30         struct FILTER_LIST_LIST *allow = NULL, *deny = NULL;
31
32
33         if (tmp_list == NULL) {
34                 tmp_list = xmalloc(sizeof(FILTER_LIST));
35                 tmp_list->dtemplate = NULL;
36                 tmp_list->allow = NULL;
37                 tmp_list->deny = NULL;
38                 tmp_list->empty = NULL;
39                 tmp_list->policy = POLICY_ALLOW;
40                 tmp_list->id = 0;
41                 tmp_list->enabled = TRUE;
42
43                 tmp_list->empty = filter_ll_new(NULL);
44
45                 filter_list = tmp_list;
46
47                 pthread_rwlock_init(&tmp_list->lock, NULL);
48         } else {
49                 allow = tmp_list->allow;
50                 deny = tmp_list->deny;
51         }
52
53         while ((xml_list = xml_section(xml_list, "<filter>"))) {
54                 XML_LIST_LOOP(xml_list, "<filter>") {
55                         XML_LIST_CMP(xml_list, "<allow>") {
56                                 allow = filter_ll_new(allow);
57                                 allow->id = filter_list->id++;
58
59                                 if (tmp_list->allow == NULL)
60                                         tmp_list->allow = allow;
61                                 XML_LIST_LOOP(xml_list, "<allow>") {
62                                         XML_LIST_CMP(xml_list, "<enabled>") {
63                                                 xml_list = xml_list->next;
64                                                 if (xml_list->type == XML_VALUE) {
65                                                         if (!strcasecmp(xml_list->item, "false"))
66                                                                 allow->enabled = FALSE;
67                                                         else
68                                                                 allow->enabled = TRUE;
69                                                 }
70                                         }
71                                         XML_LIST_CMP(xml_list, "<comment>") {
72                                                 xml_list = xml_list->next;
73                                                 if (xml_list->type == XML_VALUE)
74                                                         filter_ll_insert(allow, xml_list->item, NULL, NULL, NULL);
75                                         }
76                                         XML_LIST_CMP(xml_list, "<host>") {
77                                                 xml_list = xml_list->next;
78                                                 if (xml_list->type == XML_VALUE)
79                                                         filter_ll_insert(allow, NULL, xml_list->item, NULL, NULL);
80                                         }
81                                         XML_LIST_CMP(xml_list, "<file>") {
82                                                 xml_list = xml_list->next;
83                                                 if (xml_list->type == XML_VALUE)
84                                                         filter_ll_insert(allow, NULL, NULL, xml_list->item, NULL);
85                                         }
86                                         XML_LIST_CMP(xml_list, "<template>") {
87                                                 xml_list = xml_list->next;
88                                                 if (xml_list->type == XML_VALUE)
89                                                         filter_ll_insert(allow, NULL, NULL, NULL, xml_list->item);
90                                         }
91                                 }
92                         }
93
94                         XML_LIST_CMP(xml_list, "<deny>") {
95                                 deny = filter_ll_new(deny);
96                                 deny->id = filter_list->id++;
97
98                                 if (tmp_list->deny == NULL)
99                                         tmp_list->deny = deny;
100                                 XML_LIST_LOOP(xml_list, "<deny>") {
101                                         XML_LIST_CMP(xml_list, "<enabled>") {
102                                                 xml_list = xml_list->next;
103                                                 if (xml_list->type == XML_VALUE) {
104                                                         if (!strcasecmp(xml_list->item, "false"))
105                                                                 deny->enabled = FALSE;
106                                                         else
107                                                                 deny->enabled = TRUE;
108                                                 }
109                                         }
110                                         XML_LIST_CMP(xml_list, "<comment>") {
111                                                 xml_list = xml_list->next;
112                                                 if (xml_list->type == XML_VALUE)
113                                                         filter_ll_insert(deny, xml_list->item, NULL, NULL, NULL);
114                                         }
115                                         XML_LIST_CMP(xml_list, "<host>") {
116                                                 xml_list = xml_list->next;
117                                                 if (xml_list->type == XML_VALUE)
118                                                         filter_ll_insert(deny, NULL, xml_list->item, NULL, NULL);
119                                         }
120                                         XML_LIST_CMP(xml_list, "<file>") {
121                                                 xml_list = xml_list->next;
122                                                 if (xml_list->type == XML_VALUE)
123                                                         filter_ll_insert(deny, NULL, NULL, xml_list->item, NULL);
124                                         }
125                                         XML_LIST_CMP(xml_list, "<template>") {
126                                                 xml_list = xml_list->next;
127                                                 if (xml_list->type == XML_VALUE)
128                                                         filter_ll_insert(deny, NULL, NULL, NULL, xml_list->item);
129                                         }
130                                 }
131                         }
132                         XML_LIST_CMP(xml_list, "<enabled>") {
133                                 xml_list = xml_list->next;
134                                 if (xml_list->type == XML_VALUE) {
135                                         if (!strcasecmp(xml_list->item, "false"))
136                                                 filter_list->enabled = FALSE;
137                                         else
138                                                 filter_list->enabled = TRUE;
139                                 }
140                         }
141                         XML_LIST_CMP(xml_list, "<default>") {
142                                 xml_list = xml_list->next;
143                                 if (xml_list->type == XML_VALUE) {
144                                         FREE_AND_NULL(tmp_list->dtemplate);
145                                         tmp_list->dtemplate = xstrdup(xml_list->item);
146                                 }
147                         }
148                         XML_LIST_CMP(xml_list, "<policy>") {
149                                 xml_list = xml_list->next;
150                                 if (xml_list->type == XML_VALUE) {
151                                         if (!strcasecmp(xml_list->item, "allow"))
152                                                 tmp_list->policy = POLICY_ALLOW;
153                                         else if (!strcasecmp(xml_list->item, "deny"))
154                                                 tmp_list->policy = POLICY_DENY;
155                                 }
156                         }
157                 }
158
159         }
160
161         return filter_list;
162 }
163
164
165 XML_LIST *filter_xml(FILTER_LIST * filter_list, XML_LIST * xml_list)
166 {
167         int i;
168         char *ptr;
169         struct FILTER_LIST_LIST *fl = NULL;
170
171         if (filter_list == NULL)
172                 return xml_list;
173
174         pthread_rwlock_rdlock(&filter_list->lock);
175
176         xml_list = xml_list_add(xml_list, "<filter>", XML_TAG);
177
178         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
179         xml_list = xml_list_add(xml_list, (filter_list->enabled == TRUE) ? "true" : "false", XML_VALUE);
180         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
181
182         xml_list = xml_list_add(xml_list, "<policy>", XML_TAG);
183         xml_list = xml_list_add(xml_list, (filter_list->policy == POLICY_ALLOW) ? "allow" : "deny", XML_VALUE);
184         xml_list = xml_list_add(xml_list, "</policy>", XML_TAG);
185
186
187         if (filter_list->dtemplate != NULL) {
188                 xml_list = xml_list_add(xml_list, "<default>", XML_TAG);
189                 ptr = string_to_xml(filter_list->dtemplate);
190                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
191                 xfree(ptr);
192                 xml_list = xml_list_add(xml_list, "</default>", XML_TAG);
193
194         }
195
196         for (i = 0; i < 2; i++) {
197                 switch (i) {
198                 case 0:
199                         fl = filter_list->allow;
200                         break;
201                 case 1:
202                         fl = filter_list->deny;
203                         break;
204                 }
205
206                 for (; fl; fl = fl->next) {
207                         xml_list = xml_list_add(xml_list, (i == 0) ? "<allow>" : "<deny>", XML_TAG);
208
209                         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
210                         xml_list = xml_list_add(xml_list, (fl->enabled == TRUE) ? "true" : "false", XML_VALUE);
211                         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
212
213                         if (fl->comment != NULL) {
214                                 xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
215                                 ptr = string_to_xml(fl->comment);
216                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
217                                 xfree(ptr);
218                                 xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
219                         }
220                         if (fl->template != NULL) {
221                                 xml_list = xml_list_add(xml_list, "<template>", XML_TAG);
222                                 ptr = string_to_xml(fl->template);
223                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
224                                 xfree(ptr);
225                                 xml_list = xml_list_add(xml_list, "</template>", XML_TAG);
226
227                         }
228                         if (fl->host != NULL) {
229                                 xml_list = xml_list_add(xml_list, "<host>", XML_TAG);
230                                 ptr = string_to_xml(fl->host);
231                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
232                                 xfree(ptr);
233                                 xml_list = xml_list_add(xml_list, "</host>", XML_TAG);
234
235                         }
236                         if (fl->file != NULL) {
237                                 xml_list = xml_list_add(xml_list, "<file>", XML_TAG);
238                                 ptr = string_to_xml(fl->file);
239                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
240                                 xfree(ptr);
241                                 xml_list = xml_list_add(xml_list, "</file>", XML_TAG);
242                         }
243
244                         xml_list = xml_list_add(xml_list, (i == 0) ? "</allow>" : "</deny>", XML_TAG);
245                 }
246         }
247
248         xml_list = xml_list_add(xml_list, "</filter>", XML_TAG);
249
250         pthread_rwlock_unlock(&filter_list->lock);
251
252         return xml_list;
253 }
254
255
256 /*
257 free memory used by FILTER_LIST linked list
258 */
259 void filter_free(FILTER_LIST * filter_list)
260 {
261         filter_ll_free(filter_list->allow);
262         filter_ll_free(filter_list->deny);
263
264         FREE_AND_NULL(filter_list->dtemplate);
265         pthread_rwlock_destroy(&filter_list->lock);
266
267         xfree(filter_list);
268 }
269
270 void filter_ll_free(struct FILTER_LIST_LIST *fl)
271 {
272         struct FILTER_LIST_LIST *tmp;
273
274         while (fl != NULL) {
275                 tmp = fl->next;
276
277                 if (fl->he != NULL)
278                         reg_free(fl->he);
279                 if (fl->fe != NULL)
280                         reg_free(fl->fe);
281                 FREE_AND_NULL(fl->comment);
282                 FREE_AND_NULL(fl->host);
283                 FREE_AND_NULL(fl->file);
284                 FREE_AND_NULL(fl->template);
285
286                 xfree(fl);
287                 fl = tmp;
288         }
289 }
290
291 /*
292 determine whether or not the host and/or file is allowed to be retrieved
293 based on the rules given in the filter_list linked list
294 */
295 struct FILTER_LIST_LIST *filter_check(FILTER_LIST * filter_list, CONNECTION * connection)
296 {
297         int action = FALSE, result = TRUE, ret, i;
298         struct FILTER_LIST_LIST *current, *match = NULL;
299
300         pthread_rwlock_rdlock(&filter_list->lock);
301
302         if (filter_list->enabled == FALSE || connection->bypass & FEATURE_FILTER)
303                 return NULL;
304
305         for (i = 0; i < 2; i++) {
306                 if (i == 0) {
307                         if (filter_list->policy == POLICY_ALLOW) {
308                                 current = filter_list->deny;
309                                 action = FALSE;
310                                 result = TRUE;
311                         } else {
312                                 current = filter_list->allow;
313                                 action = TRUE;
314                                 result = FALSE;
315                         }
316                 } else if (result == action) {
317                         if (filter_list->policy == POLICY_ALLOW) {
318                                 current = filter_list->allow;
319                                 action = TRUE;
320                         } else {
321                                 current = filter_list->deny;
322                                 action = FALSE;
323                         }
324                 } else
325                         break;
326
327                 for (; current != NULL; current = current->next) {
328                         if (current->enabled == FALSE)
329                                 continue;
330
331                         if (current->he != NULL) {
332                                 ret = reg_exec(current->he, connection->header->host);
333                                 if (ret)
334                                         continue;
335                         }
336
337                         if (current->fe != NULL) {
338                                 ret = reg_exec(current->fe, connection->header->file);
339                                 if (ret)
340                                         continue;
341                         }
342
343                         match = current;
344                         result = action;
345                         break;
346                 }
347         }
348
349         return (!result) ? (match != NULL) ? match : filter_list->empty : NULL;
350 }
351
352 void filter_ll_insert(struct FILTER_LIST_LIST *x, char *a, char *b, char *c, char *d)
353 {
354         if (a != NULL) {
355                 FREE_AND_NULL(x->comment);
356
357                 if (strcmp(a, ""))
358                         x->comment = xstrdup(a);
359         }
360         if (b != NULL) {
361                 if (x->he != NULL)
362                         reg_free(x->he);
363                 FREE_AND_NULL(x->host);
364
365                 if (strcmp(b, "")) {
366                         x->he = reg_compile(b, REGFLAGS);
367                         x->host = xstrdup(b);
368                 } else
369                         x->he = NULL;
370         }
371
372         if (c != NULL) {
373                 if (x->fe != NULL)
374                         reg_free(x->fe);
375                 FREE_AND_NULL(x->file);
376
377                 if (strcmp(c, "")) {
378                         x->fe = reg_compile(c, REGFLAGS);
379                         x->file = xstrdup(c);
380                 } else
381                         x->fe = NULL;
382         }
383
384         if (d != NULL) {
385                 FREE_AND_NULL(x->template);
386
387                 if (strcmp(d, ""))
388                         x->template = xstrdup(d);
389         }
390 }
391
392 struct FILTER_LIST_LIST *filter_ll_delete(struct FILTER_LIST_LIST *x)
393 {
394         struct FILTER_LIST_LIST *start = x;
395
396         while (start->prev != NULL)
397                 start = start->prev;
398
399         if (x->next != NULL)
400                 x->next->prev = x->prev;
401         if (x->prev != NULL)
402                 x->prev->next = x->next;
403         else
404                 start = start->next;
405
406         if (x->he != NULL)
407                 reg_free(x->he);
408         if (x->fe != NULL)
409                 reg_free(x->fe);
410         FREE_AND_NULL(x->comment);
411         FREE_AND_NULL(x->host);
412         FREE_AND_NULL(x->file);
413         FREE_AND_NULL(x->template);
414
415         xfree(x);
416
417         return start;
418 }
419
420 struct FILTER_LIST_LIST *filter_ll_new(struct FILTER_LIST_LIST *x)
421 {
422         if (x == NULL) {
423                 x = xmalloc(sizeof(struct FILTER_LIST_LIST));
424                 x->prev = NULL;
425         } else {
426                 while (x->next != NULL)
427                         x = x->next;
428                 x->next = xmalloc(sizeof(struct FILTER_LIST_LIST));
429                 x->next->prev = x;
430                 x = x->next;
431         }
432         x->next = NULL;
433         x->enabled = TRUE;
434         x->comment = NULL;
435         x->he = NULL;
436         x->fe = NULL;
437         x->host = NULL;
438         x->file = NULL;
439         x->template = NULL;
440
441         return x;
442 }
443