Removed threading
[middleman.git] / src / access.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 parse <access> section from XML file
26 */
27 ACCESS_LIST *access_load(ACCESS_LIST * access_list, XML_LIST * xml_list)
28 {
29         ACCESS_LIST *tmp_list = access_list;
30         struct ACCESS_LIST_LIST *allow = NULL, *deny = NULL;
31
32         if (tmp_list == NULL) {
33                 tmp_list = xmalloc(sizeof(ACCESS_LIST));
34                 tmp_list->allow = NULL;
35                 tmp_list->deny = NULL;
36                 tmp_list->policy = POLICY_DENY;
37                 tmp_list->id = 0;
38
39                 access_list = tmp_list;
40
41                 pthread_rwlock_init(&tmp_list->lock, NULL);
42         } else {
43                 allow = tmp_list->allow;
44                 deny = tmp_list->deny;
45         }
46
47         while ((xml_list = xml_section(xml_list, "<access>"))) {
48                 XML_LIST_LOOP(xml_list, "<access>") {
49                         XML_LIST_CMP(xml_list, "<allow>") {
50                                 allow = access_ll_new(allow);
51                                 allow->id = access_list->id++;
52
53                                 if (tmp_list->allow == NULL)
54                                         tmp_list->allow = allow;
55                                 XML_LIST_LOOP(xml_list, "<allow>") {
56                                         XML_LIST_CMP(xml_list, "<enabled>") {
57                                                 xml_list = xml_list->next;
58                                                 if (xml_list->type == XML_VALUE) {
59                                                         if (!strcasecmp(xml_list->item, "false"))
60                                                                 allow->enabled = FALSE;
61                                                         else
62                                                                 allow->enabled = TRUE;
63                                                 }
64                                         }
65                                         XML_LIST_CMP(xml_list, "<comment>") {
66                                                 xml_list = xml_list->next;
67                                                 if (xml_list->type == XML_VALUE)
68                                                         access_ll_insert(allow, xml_list->item, NULL, NULL, NULL, NULL, NULL);
69                                         }
70                                         XML_LIST_CMP(xml_list, "<ip>") {
71                                                 xml_list = xml_list->next;
72                                                 if (xml_list->type == XML_VALUE)
73                                                         access_ll_insert(allow, NULL, xml_list->item, NULL, NULL, NULL, NULL);
74                                         }
75                                         XML_LIST_CMP(xml_list, "<access>") {
76                                                 xml_list = xml_list->next;
77                                                 if (xml_list->type == XML_VALUE)
78                                                         access_ll_insert(allow, NULL, NULL, xml_list->item, NULL, NULL, NULL);
79                                         }
80                                         XML_LIST_CMP(xml_list, "<bypass>") {
81                                                 xml_list = xml_list->next;
82                                                 if (xml_list->type == XML_VALUE)
83                                                         access_ll_insert(allow, NULL, NULL, NULL, xml_list->item, NULL, NULL);
84                                         }
85                                         XML_LIST_CMP(xml_list, "<username>") {
86                                                 xml_list = xml_list->next;
87                                                 if (xml_list->type == XML_VALUE)
88                                                         access_ll_insert(allow, NULL, NULL, NULL, NULL, xml_list->item, NULL);
89                                         }
90                                         XML_LIST_CMP(xml_list, "<password>") {
91                                                 xml_list = xml_list->next;
92                                                 if (xml_list->type == XML_VALUE)
93                                                         access_ll_insert(allow, NULL, NULL, NULL, NULL, NULL, xml_list->item);
94                                         }
95                                 }
96                         }
97                         XML_LIST_CMP(xml_list, "<deny>") {
98                                 deny = access_ll_new(deny);
99                                 deny->id = access_list->id++;
100
101                                 if (tmp_list->deny == NULL)
102                                         tmp_list->deny = deny;
103                                 XML_LIST_LOOP(xml_list, "<deny>") {
104                                         XML_LIST_CMP(xml_list, "<enabled>") {
105                                                 xml_list = xml_list->next;
106                                                 if (xml_list->type == XML_VALUE) {
107                                                         if (!strcasecmp(xml_list->item, "false"))
108                                                                 deny->enabled = FALSE;
109                                                         else
110                                                                 deny->enabled = TRUE;
111                                                 }
112                                         }
113                                         XML_LIST_CMP(xml_list, "<comment>") {
114                                                 xml_list = xml_list->next;
115                                                 if (xml_list->type == XML_VALUE)
116                                                         access_ll_insert(deny, xml_list->item, NULL, NULL, NULL, NULL, NULL);
117                                         }
118                                         XML_LIST_CMP(xml_list, "<ip>") {
119                                                 xml_list = xml_list->next;
120                                                 if (xml_list->type == XML_VALUE)
121                                                         access_ll_insert(deny, NULL, xml_list->item, NULL, NULL, NULL, NULL);
122                                         }
123                                         XML_LIST_CMP(xml_list, "<access>") {
124                                                 xml_list = xml_list->next;
125                                                 if (xml_list->type == XML_VALUE)
126                                                         access_ll_insert(deny, NULL, NULL, xml_list->item, NULL, NULL, NULL);
127                                         }
128                                         XML_LIST_CMP(xml_list, "<bypass>") {
129                                                 xml_list = xml_list->next;
130                                                 if (xml_list->type == XML_VALUE)
131                                                         access_ll_insert(deny, NULL, NULL, NULL, xml_list->item, NULL, NULL);
132                                         }
133                                         XML_LIST_CMP(xml_list, "<username>") {
134                                                 xml_list = xml_list->next;
135                                                 if (xml_list->type == XML_VALUE)
136                                                         access_ll_insert(deny, NULL, NULL, NULL, NULL, xml_list->item, NULL);
137                                         }
138                                         XML_LIST_CMP(xml_list, "<password>") {
139                                                 xml_list = xml_list->next;
140                                                 if (xml_list->type == XML_VALUE)
141                                                         access_ll_insert(deny, NULL, NULL, NULL, NULL, NULL, xml_list->item);
142                                         }
143                                 }
144                         }
145                         XML_LIST_CMP(xml_list, "<policy>") {
146                                 xml_list = xml_list->next;
147                                 if (xml_list->type == XML_VALUE) {
148                                         if (!strcasecmp(xml_list->item, "allow"))
149                                                 tmp_list->policy = POLICY_ALLOW;
150                                         else if (!strcasecmp(xml_list->item, "deny"))
151                                                 tmp_list->policy = POLICY_DENY;
152                                 }
153                         }
154                 }
155         }
156
157         return access_list;
158 }
159
160 XML_LIST *access_xml(ACCESS_LIST * access_list, XML_LIST * xml_list)
161 {
162         int i;
163         char *ptr, buf[128];
164         struct ACCESS_LIST_LIST *al = NULL;
165
166         if (access_list == NULL)
167                 return xml_list;
168
169         pthread_rwlock_rdlock(&access_list->lock);
170
171         xml_list = xml_list_add(xml_list, "<access>", XML_TAG);
172
173         xml_list = xml_list_add(xml_list, "<policy>", XML_TAG);
174         xml_list = xml_list_add(xml_list, (access_list->policy == POLICY_ALLOW) ? "allow" : "deny", XML_VALUE);
175         xml_list = xml_list_add(xml_list, "</policy>", XML_TAG);
176
177         for (i = 0; i < 2; i++) {
178                 switch (i) {
179                 case 0:
180                         al = access_list->allow;
181                         break;
182                 case 1:
183                         al = access_list->deny;
184                         break;
185                 }
186
187                 for (; al; al = al->next) {
188                         xml_list = xml_list_add(xml_list, (i == 0) ? "<allow>" : "<deny>", XML_TAG);
189
190                         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
191                         xml_list = xml_list_add(xml_list, (al->enabled == TRUE) ? "true" : "false", XML_VALUE);
192                         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
193
194                         if (al->comment != NULL) {
195                                 xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
196                                 ptr = string_to_xml(al->comment);
197                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
198                                 xfree(ptr);
199                                 xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
200                         }
201                         if (al->ip != NULL) {
202                                 xml_list = xml_list_add(xml_list, "<ip>", XML_TAG);
203                                 ptr = string_to_xml(al->ip);
204                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
205                                 xfree(ptr);
206                                 xml_list = xml_list_add(xml_list, "</ip>", XML_TAG);
207                         }
208                         if (al->username != NULL) {
209                                 xml_list = xml_list_add(xml_list, "<username>", XML_TAG);
210                                 ptr = string_to_xml(al->username);
211                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
212                                 xfree(ptr);
213                                 xml_list = xml_list_add(xml_list, "</username>", XML_TAG);
214                         }
215                         if (al->password != NULL) {
216                                 xml_list = xml_list_add(xml_list, "<password>", XML_TAG);
217                                 ptr = string_to_xml(al->password);
218                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
219                                 xfree(ptr);
220                                 xml_list = xml_list_add(xml_list, "</password>", XML_TAG);
221                         }
222                         if (al->bypass != 0) {
223                                 xml_list = xml_list_add(xml_list, "<bypass>", XML_TAG);
224                                 snprintf(buf, sizeof(buf), "%s,%s,%s,%s,%s,%s,%s,%s,%s", (al->bypass & FEATURE_FILTER) ? "filter" : "", (al->bypass & FEATURE_HEADER) ? "header" : "", (al->bypass & FEATURE_MIME) ? "mime" : "", (al->bypass & FEATURE_REDIRECT) ? "redirect" : "", (al->bypass & FEATURE_COOKIES) ? "cookies" : "", (al->bypass & FEATURE_REWRITE) ? "rewrite" : "", (al->bypass & FEATURE_EXTERNAL) ? "external" : "", (al->bypass & FEATURE_FORWARD) ? "forward" : "", (al->bypass & FEATURE_KEYWORDS) ? "keywords" : "");
225                                 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
226                                 xml_list = xml_list_add(xml_list, "</bypass>", XML_TAG);
227                         }
228
229                         xml_list = xml_list_add(xml_list, "<access>", XML_TAG);
230                         snprintf(buf, sizeof(buf), "%s,%s,%s,%s,%s,%s", (al->access & ACCESS_CONFIG) ? "config" : "", (al->access & ACCESS_PROXY) ? "proxy" : "", (al->access & ACCESS_CONNECT) ? "connect" : "", (al->access & ACCESS_HTTP) ? "http" : "", (al->access & ACCESS_TRANSPARENT) ? "transparent" : "", (al->access & ACCESS_BYPASS) ? "bypass" : "");
231                         xml_list = xml_list_add(xml_list, buf, XML_VALUE);
232                         xml_list = xml_list_add(xml_list, "</access>", XML_TAG);
233
234                         xml_list = xml_list_add(xml_list, (i == 0) ? "</allow>" : "</deny>", XML_TAG);
235                 }
236         }
237
238         xml_list = xml_list_add(xml_list, "</access>", XML_TAG);
239
240         pthread_rwlock_unlock(&access_list->lock);
241
242         return xml_list;
243 }
244
245 /*
246 free memory used by ACCESS_LIST linked list
247 */
248 void access_free(ACCESS_LIST * access_list)
249 {
250         if (!access_list)
251                 return;
252
253         access_ll_free(access_list->allow);
254         access_ll_free(access_list->deny);
255
256         pthread_rwlock_destroy(&access_list->lock);
257
258         xfree(access_list);
259 }
260
261 void access_ll_free(struct ACCESS_LIST_LIST *al)
262 {
263         struct ACCESS_LIST_LIST *tmp;
264
265         while (al != NULL) {
266                 tmp = al->next;
267
268                 if (al->ie != NULL)
269                         reg_free(al->ie);
270                 FREE_AND_NULL(al->comment);
271                 FREE_AND_NULL(al->ip);
272                 FREE_AND_NULL(al->username);
273                 FREE_AND_NULL(al->password);
274
275                 xfree(al);
276                 al = tmp;
277         }
278 }
279
280 /*
281 check whether or not an ip address is allowed access based on the rules supplied
282 in the access_list linked list
283 */
284 int access_check(ACCESS_LIST * access_list, CONNECTION * connection, char *username, char *password)
285 {
286         int action = FALSE, result = TRUE, i, ret;
287         struct ACCESS_LIST_LIST *current = NULL;
288
289         if (!access_list)
290                 return 0;
291
292         pthread_rwlock_rdlock(&access_list->lock);
293
294         for (i = 0; i < 2; i++) {
295                 if (i == 0) {
296                         if (access_list->policy == POLICY_ALLOW) {
297                                 current = access_list->deny;
298                                 action = FALSE;
299                                 result = TRUE;
300                         } else {
301                                 current = access_list->allow;
302                                 action = TRUE;
303                                 result = FALSE;
304                         }
305                 } else if (result == action) {
306                         if (access_list->policy == POLICY_ALLOW) {
307                                 current = access_list->allow;
308                                 action = TRUE;
309                         } else {
310                                 current = access_list->deny;
311                                 action = FALSE;
312                         }
313                 } else
314                         break;
315
316                 for (; current != NULL; current = current->next) {
317                         if (current->enabled == FALSE)
318                                 continue;
319
320                         if (current->ie != NULL) {
321                                 ret = reg_exec(current->ie, connection->ip);
322                                 if (ret)
323                                         continue;
324                         }
325
326                         if (current->username != NULL) {
327                                 if (username != NULL) {
328                                         if (strcmp(current->username, username))
329                                                 continue;
330                                 } else connection->authenticate = TRUE;
331                         } else if (username == NULL)
332                                 connection->authenticate = FALSE;
333
334                         if (current->password != NULL && password != NULL && strcmp(current->password, password))
335                                 continue;
336
337                         connection->access = current->access;
338                         connection->obypass = current->bypass;
339
340                         result = action;
341                         break;
342                 }
343         }
344
345         if (result && current == NULL) {
346                 /* policy is allow, and no deny rules matched; allow full access */
347                 connection->access = ~0;
348         }
349
350         pthread_rwlock_unlock(&access_list->lock);
351
352         return result;
353 }
354
355 void access_ll_insert(struct ACCESS_LIST_LIST *x, char *a, char *b, char *c, char *d, char *e, char *f)
356 {
357         int i;
358         char **args;
359
360         if (a != NULL) {
361                 FREE_AND_NULL(x->comment);
362
363                 if (strcmp(a, ""))
364                         x->comment = xstrdup(a);
365         }
366         if (b != NULL) {
367                 if (x->ie != NULL)
368                         reg_free(x->ie);
369                 FREE_AND_NULL(x->ip);
370
371                 if (strcmp(b, "")) {
372                         x->ip = xstrdup(b);
373                         x->ie = reg_compile(b, REGFLAGS);
374                 } else
375                         x->ie = NULL;
376         }
377         if (c != NULL) {
378                 x->access = 0;
379
380                 if (strcmp(c, "")) {
381                         args = string_break(c, ',');
382                         for (i = 0; args[i]; i++) {
383                                 if (!strcasecmp(args[i], "config"))
384                                         x->access |= ACCESS_CONFIG;
385                                 else if (!strcasecmp(args[i], "proxy"))
386                                         x->access |= ACCESS_PROXY;
387                                 else if (!strcasecmp(args[i], "connect"))
388                                         x->access |= ACCESS_CONNECT;
389                                 else if (!strcasecmp(args[i], "http"))
390                                         x->access |= ACCESS_HTTP;
391                                 else if (!strcasecmp(args[i], "transparent"))
392                                         x->access |= ACCESS_TRANSPARENT;
393                                 else if (!strcasecmp(args[i], "bypass"))
394                                         x->access |= ACCESS_BYPASS;
395                                 xfree(args[i]);
396                         }
397                         xfree(args);
398                 }
399         }
400         if (d != NULL) {
401                 x->bypass = 0;
402
403                 if (strcmp(d, "")) {
404                         args = string_break(d, ',');
405                         for (i = 0; args[i]; i++) {
406                                 if (!strcasecmp(args[i], "filter"))
407                                         x->bypass |= FEATURE_FILTER;
408                                 else if (!strcasecmp(args[i], "header"))
409                                         x->bypass |= FEATURE_HEADER;
410                                 else if (!strcasecmp(args[i], "mime"))
411                                         x->bypass |= FEATURE_MIME;
412                                 else if (!strcasecmp(args[i], "redirect"))
413                                         x->bypass |= FEATURE_REDIRECT;
414                                 else if (!strcasecmp(args[i], "cookies"))
415                                         x->bypass |= FEATURE_COOKIES;
416                                 else if (!strcasecmp(args[i], "rewrite"))
417                                         x->bypass |= FEATURE_REWRITE;
418                                 else if (!strcasecmp(args[i], "external"))
419                                         x->bypass |= FEATURE_EXTERNAL;
420                                 else if (!strcasecmp(args[i], "forward"))
421                                         x->bypass |= FEATURE_FORWARD;
422                                 else if (!strcasecmp(args[i], "keywords"))
423                                         x->bypass |= FEATURE_KEYWORDS;
424                                 xfree(args[i]);
425                         }
426                         xfree(args);
427                 }
428         }
429         if (e != NULL) {
430                 FREE_AND_NULL(x->username);
431
432                 if (strcmp(e, ""))
433                         x->username = xstrdup(e);
434         }
435         if (f != NULL) {
436                 FREE_AND_NULL(x->password);
437
438                 if (strcmp(f, ""))
439                         x->password = xstrdup(f);
440         }
441 }
442
443 struct ACCESS_LIST_LIST *access_ll_new(struct ACCESS_LIST_LIST *x)
444 {
445         if (x == NULL) {
446                 x = xmalloc(sizeof(struct ACCESS_LIST_LIST));
447                 x->prev = NULL;
448         } else {
449                 while (x->next != NULL)
450                         x = x->next;
451                 x->next = xmalloc(sizeof(struct ACCESS_LIST_LIST));
452                 x->next->prev = x;
453                 x = x->next;
454         }
455
456         x->enabled = TRUE;
457         x->comment = NULL;
458         x->ie = NULL;
459         x->ip = NULL;
460         x->username = NULL;
461         x->password = NULL;
462         x->access = 0;
463         x->bypass = 0;
464         x->next = NULL;
465
466         return x;
467 }
468
469 struct ACCESS_LIST_LIST *access_ll_delete(struct ACCESS_LIST_LIST *x)
470 {
471         struct ACCESS_LIST_LIST *start = x;
472
473         while (start->prev != NULL)
474                 start = start->prev;
475
476         if (x->next != NULL)
477                 x->next->prev = x->prev;
478         if (x->prev != NULL)
479                 x->prev->next = x->next;
480         else
481                 start = start->next;
482
483         if (x->ie != NULL)
484                 reg_free(x->ie);
485         FREE_AND_NULL(x->comment);
486         FREE_AND_NULL(x->ip);
487         FREE_AND_NULL(x->username);
488         FREE_AND_NULL(x->password);
489
490         xfree(x);
491
492         return start;
493 }