/* MiddleMan filtering proxy server Copyright (C) 2002 Jason McLaughlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "proto.h" /* parse section from XML file */ ACCESS_LIST *access_load(ACCESS_LIST * access_list, XML_LIST * xml_list) { ACCESS_LIST *tmp_list = access_list; struct ACCESS_LIST_LIST *allow = NULL, *deny = NULL; if (tmp_list == NULL) { tmp_list = xmalloc(sizeof(ACCESS_LIST)); tmp_list->allow = NULL; tmp_list->deny = NULL; tmp_list->policy = POLICY_DENY; tmp_list->id = 0; access_list = tmp_list; pthread_rwlock_init(&tmp_list->lock, NULL); } else { allow = tmp_list->allow; deny = tmp_list->deny; } while ((xml_list = xml_section(xml_list, ""))) { XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { allow = access_ll_new(allow); allow->id = access_list->id++; if (tmp_list->allow == NULL) tmp_list->allow = allow; XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "false")) allow->enabled = FALSE; else allow->enabled = TRUE; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(allow, xml_list->item, NULL, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(allow, NULL, xml_list->item, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(allow, NULL, NULL, xml_list->item, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(allow, NULL, NULL, NULL, xml_list->item, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(allow, NULL, NULL, NULL, NULL, xml_list->item, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(allow, NULL, NULL, NULL, NULL, NULL, xml_list->item); } } } XML_LIST_CMP(xml_list, "") { deny = access_ll_new(deny); deny->id = access_list->id++; if (tmp_list->deny == NULL) tmp_list->deny = deny; XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "false")) deny->enabled = FALSE; else deny->enabled = TRUE; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(deny, xml_list->item, NULL, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(deny, NULL, xml_list->item, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(deny, NULL, NULL, xml_list->item, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(deny, NULL, NULL, NULL, xml_list->item, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(deny, NULL, NULL, NULL, NULL, xml_list->item, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) access_ll_insert(deny, NULL, NULL, NULL, NULL, NULL, xml_list->item); } } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "allow")) tmp_list->policy = POLICY_ALLOW; else if (!strcasecmp(xml_list->item, "deny")) tmp_list->policy = POLICY_DENY; } } } } return access_list; } XML_LIST *access_xml(ACCESS_LIST * access_list, XML_LIST * xml_list) { int i; char *ptr, buf[128]; struct ACCESS_LIST_LIST *al = NULL; if (access_list == NULL) return xml_list; pthread_rwlock_rdlock(&access_list->lock); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, (access_list->policy == POLICY_ALLOW) ? "allow" : "deny", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); for (i = 0; i < 2; i++) { switch (i) { case 0: al = access_list->allow; break; case 1: al = access_list->deny; break; } for (; al; al = al->next) { xml_list = xml_list_add(xml_list, (i == 0) ? "" : "", XML_TAG); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, (al->enabled == TRUE) ? "true" : "false", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); if (al->comment != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(al->comment); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (al->ip != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(al->ip); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (al->username != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(al->username); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (al->password != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(al->password); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (al->bypass != 0) { xml_list = xml_list_add(xml_list, "", XML_TAG); 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" : ""); xml_list = xml_list_add(xml_list, buf, XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); } xml_list = xml_list_add(xml_list, "", XML_TAG); 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" : ""); xml_list = xml_list_add(xml_list, buf, XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, (i == 0) ? "" : "", XML_TAG); } } xml_list = xml_list_add(xml_list, "", XML_TAG); pthread_rwlock_unlock(&access_list->lock); return xml_list; } /* free memory used by ACCESS_LIST linked list */ void access_free(ACCESS_LIST * access_list) { if (!access_list) return; access_ll_free(access_list->allow); access_ll_free(access_list->deny); pthread_rwlock_destroy(&access_list->lock); xfree(access_list); } void access_ll_free(struct ACCESS_LIST_LIST *al) { struct ACCESS_LIST_LIST *tmp; while (al != NULL) { tmp = al->next; if (al->ie != NULL) reg_free(al->ie); FREE_AND_NULL(al->comment); FREE_AND_NULL(al->ip); FREE_AND_NULL(al->username); FREE_AND_NULL(al->password); xfree(al); al = tmp; } } /* check whether or not an ip address is allowed access based on the rules supplied in the access_list linked list */ int access_check(ACCESS_LIST * access_list, CONNECTION * connection, char *username, char *password) { int action = FALSE, result = TRUE, i, ret; struct ACCESS_LIST_LIST *current = NULL; if (!access_list) return 0; pthread_rwlock_rdlock(&access_list->lock); for (i = 0; i < 2; i++) { if (i == 0) { if (access_list->policy == POLICY_ALLOW) { current = access_list->deny; action = FALSE; result = TRUE; } else { current = access_list->allow; action = TRUE; result = FALSE; } } else if (result == action) { if (access_list->policy == POLICY_ALLOW) { current = access_list->allow; action = TRUE; } else { current = access_list->deny; action = FALSE; } } else break; for (; current != NULL; current = current->next) { if (current->enabled == FALSE) continue; if (current->ie != NULL) { ret = reg_exec(current->ie, connection->ip); if (ret) continue; } if (current->username != NULL) { if (username != NULL) { if (strcmp(current->username, username)) continue; } else connection->authenticate = TRUE; } else if (username == NULL) connection->authenticate = FALSE; if (current->password != NULL && password != NULL && strcmp(current->password, password)) continue; connection->access = current->access; connection->obypass = current->bypass; result = action; break; } } if (result && current == NULL) { /* policy is allow, and no deny rules matched; allow full access */ connection->access = ~0; } pthread_rwlock_unlock(&access_list->lock); return result; } void access_ll_insert(struct ACCESS_LIST_LIST *x, char *a, char *b, char *c, char *d, char *e, char *f) { int i; char **args; if (a != NULL) { FREE_AND_NULL(x->comment); if (strcmp(a, "")) x->comment = xstrdup(a); } if (b != NULL) { if (x->ie != NULL) reg_free(x->ie); FREE_AND_NULL(x->ip); if (strcmp(b, "")) { x->ip = xstrdup(b); x->ie = reg_compile(b, REGFLAGS); } else x->ie = NULL; } if (c != NULL) { x->access = 0; if (strcmp(c, "")) { args = string_break(c, ','); for (i = 0; args[i]; i++) { if (!strcasecmp(args[i], "config")) x->access |= ACCESS_CONFIG; else if (!strcasecmp(args[i], "proxy")) x->access |= ACCESS_PROXY; else if (!strcasecmp(args[i], "connect")) x->access |= ACCESS_CONNECT; else if (!strcasecmp(args[i], "http")) x->access |= ACCESS_HTTP; else if (!strcasecmp(args[i], "transparent")) x->access |= ACCESS_TRANSPARENT; else if (!strcasecmp(args[i], "bypass")) x->access |= ACCESS_BYPASS; xfree(args[i]); } xfree(args); } } if (d != NULL) { x->bypass = 0; if (strcmp(d, "")) { args = string_break(d, ','); for (i = 0; args[i]; i++) { if (!strcasecmp(args[i], "filter")) x->bypass |= FEATURE_FILTER; else if (!strcasecmp(args[i], "header")) x->bypass |= FEATURE_HEADER; else if (!strcasecmp(args[i], "mime")) x->bypass |= FEATURE_MIME; else if (!strcasecmp(args[i], "redirect")) x->bypass |= FEATURE_REDIRECT; else if (!strcasecmp(args[i], "cookies")) x->bypass |= FEATURE_COOKIES; else if (!strcasecmp(args[i], "rewrite")) x->bypass |= FEATURE_REWRITE; else if (!strcasecmp(args[i], "external")) x->bypass |= FEATURE_EXTERNAL; else if (!strcasecmp(args[i], "forward")) x->bypass |= FEATURE_FORWARD; else if (!strcasecmp(args[i], "keywords")) x->bypass |= FEATURE_KEYWORDS; xfree(args[i]); } xfree(args); } } if (e != NULL) { FREE_AND_NULL(x->username); if (strcmp(e, "")) x->username = xstrdup(e); } if (f != NULL) { FREE_AND_NULL(x->password); if (strcmp(f, "")) x->password = xstrdup(f); } } struct ACCESS_LIST_LIST *access_ll_new(struct ACCESS_LIST_LIST *x) { if (x == NULL) { x = xmalloc(sizeof(struct ACCESS_LIST_LIST)); x->prev = NULL; } else { while (x->next != NULL) x = x->next; x->next = xmalloc(sizeof(struct ACCESS_LIST_LIST)); x->next->prev = x; x = x->next; } x->enabled = TRUE; x->comment = NULL; x->ie = NULL; x->ip = NULL; x->username = NULL; x->password = NULL; x->access = 0; x->bypass = 0; x->next = NULL; return x; } struct ACCESS_LIST_LIST *access_ll_delete(struct ACCESS_LIST_LIST *x) { struct ACCESS_LIST_LIST *start = x; while (start->prev != NULL) start = start->prev; if (x->next != NULL) x->next->prev = x->prev; if (x->prev != NULL) x->prev->next = x->next; else start = start->next; if (x->ie != NULL) reg_free(x->ie); FREE_AND_NULL(x->comment); FREE_AND_NULL(x->ip); FREE_AND_NULL(x->username); FREE_AND_NULL(x->password); xfree(x); return start; }