>From 5368532099b8e8c2c6970df8a2d1463a7eaa72bc Mon Sep 17 00:00:00 2001
From: Krzysztof Piotr Oledzki <[email protected]>
Date: Tue, 27 Jan 2009 16:47:15 +0100
Subject: [MEDIUM] access control (block) rework - rfc

Allow more advanced access control:
 - rename 'block' to 'deny'
 - implement 'allow'
 - implement 'order': allow,deny and deny,allow (default)
 - implement new check_cond_list() helper function

RFC quality patch.

TODO: documentation
---
 include/proto/acl.h   |    5 +++++
 include/types/proxy.h |    7 ++++++-
 src/acl.c             |   19 +++++++++++++++++++
 src/cfgparse.c        |   33 +++++++++++++++++++++++++++++----
 src/haproxy.c         |    8 +++++++-
 src/proto_http.c      |   39 +++++++++++++++++++++++++--------------
 6 files changed, 91 insertions(+), 20 deletions(-)

diff --git a/include/proto/acl.h b/include/proto/acl.h
index fffce48..8ccc8f0 100644
--- a/include/proto/acl.h
+++ b/include/proto/acl.h
@@ -92,6 +92,11 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, 
struct session *l4, v
  */
 struct acl *cond_find_require(struct acl_cond *cond, unsigned int require);
 
+/* Walk through <cond_list> and return 0 if all tests fail or 1 if at last one 
test succeeds.
+ * This function checks the polarity required by IF/UNLESS.
+ */
+int check_cond_list(struct list *cond_list, struct proxy *px, struct session 
*l4, void *l7, int dir);
+
 /* Return a pointer to the ACL <name> within the list starting at <head>, or
  * NULL if not found.
  */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 5d0869c..b70a7e4 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -112,6 +112,9 @@
 #define PR_O2_SPLIC_AUT        0x00000004      /* automatically use linux 
kernel's splice() */
 #define PR_O2_SPLIC_ANY        
(PR_O2_SPLIC_REQ|PR_O2_SPLIC_RTR|PR_O2_SPLIC_AUT)
 
+#define PR_ORDER_DENY_ALLOW    0       /* last type of match wins: first 
evaluate deny_cond then allow_cond, allow by default */
+#define PR_ORDER_ALLOW_DENY    1       /* last type of match wins: first 
evaluate allow_cond then deny_cond, deny by default */
+
 /* This structure is used to apply fast weighted round robin on a server group 
*/
 struct fwrr_group {
        struct eb_root curr;    /* tree for servers in "current" time range */
@@ -130,13 +133,15 @@ struct proxy {
        int options;                            /* PR_O_REDISP, PR_O_TRANSP, 
... */
        int options2;                           /* PR_O2_* */
        int mode;                               /* mode = PR_MODE_TCP, 
PR_MODE_HTTP or PR_MODE_HEALTH */
+       int order;                              /* order = PR_ORDER_DENY_ALLOW 
or PR_ORDER_ALLOW_DENY */
        struct sockaddr_in dispatch_addr;       /* the default address to 
connect to */
        union {
                struct proxy *be;               /* default backend, or NULL if 
none set */
                char *name;                     /* default backend name during 
config parse */
        } defbe;
        struct list acl;                        /* ACL declared on this proxy */
-       struct list block_cond;                 /* early blocking conditions 
(chained) */
+       struct list allow_cond;                 /* early allow blocking 
conditions (chained) */
+       struct list deny_cond;                  /* early deny blocking 
conditions (chained) */
        struct list redirect_rules;             /* content redirecting rules 
(chained) */
        struct list switching_rules;            /* content switching rules 
(chained) */
        struct {                                /* TCP request processing */
diff --git a/src/acl.c b/src/acl.c
index ed41e91..8cab8df 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1119,6 +1119,25 @@ struct acl *cond_find_require(struct acl_cond *cond, 
unsigned int require)
        return NULL;
 }
 
+/* Walk through <cond_list> and return 0 if all tests fail or 1 if at last one 
test succeeds.
+ * This function checks the polarity required by IF/UNLESS.
+ */
+int check_cond_list(struct list *cond_list, struct proxy *px, struct session 
*l4, void *l7, int dir) {
+
+       struct acl_cond *cond;
+
+       list_for_each_entry(cond, cond_list, list) {
+               int ret = acl_exec_cond(cond, px, l4, l7, dir);
+
+               if (cond->pol == ACL_COND_UNLESS)
+                       ret = !ret;
+
+               if (ret)
+                       return 1;
+       }
+
+       return 0;
+}
 
 /************************************************************************/
 /*             All supported keywords must be declared here.            */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index f98d07e..46f4ba7 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -561,13 +561,15 @@ static void init_default_instance()
        memset(&defproxy, 0, sizeof(defproxy));
        defproxy.mode = PR_MODE_TCP;
        defproxy.state = PR_STNEW;
+       defproxy.order = PR_ORDER_DENY_ALLOW;
        defproxy.maxconn = cfg_maxpconn;
        defproxy.conn_retries = CONN_RETRIES;
        defproxy.logfac1 = defproxy.logfac2 = -1; /* log disabled */
 
        LIST_INIT(&defproxy.pendconns);
        LIST_INIT(&defproxy.acl);
-       LIST_INIT(&defproxy.block_cond);
+       LIST_INIT(&defproxy.allow_cond);
+       LIST_INIT(&defproxy.deny_cond);
        LIST_INIT(&defproxy.mon_fail_cond);
        LIST_INIT(&defproxy.switching_rules);
 
@@ -641,7 +643,8 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int inv)
                proxy = curproxy;
                LIST_INIT(&curproxy->pendconns);
                LIST_INIT(&curproxy->acl);
-               LIST_INIT(&curproxy->block_cond);
+               LIST_INIT(&curproxy->allow_cond);
+               LIST_INIT(&curproxy->deny_cond);
                LIST_INIT(&curproxy->redirect_rules);
                LIST_INIT(&curproxy->mon_fail_cond);
                LIST_INIT(&curproxy->switching_rules);
@@ -668,6 +671,7 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int inv)
                curproxy->state = defproxy.state;
                curproxy->options = defproxy.options;
                curproxy->options2 = defproxy.options2;
+               curproxy->order = defproxy.order;
                curproxy->lbprm.algo = defproxy.lbprm.algo;
                curproxy->except_net = defproxy.except_net;
                curproxy->except_mask = defproxy.except_mask;
@@ -1109,7 +1113,19 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int inv)
                }
                curproxy->conn_retries = atol(args[1]);
        }
-       else if (!strcmp(args[0], "block")) {  /* early blocking based on ACLs 
*/
+       else if (!strcmp(args[0], "order")) {
+               if (!strcasecmp(args[1], "allow,deny"))
+                       curproxy->order = PR_ORDER_ALLOW_DENY;
+               else if (!strcasecmp(args[1], "deny,allow"))
+                       curproxy->order = PR_ORDER_DENY_ALLOW;
+               else {
+                       Alert("parsing [%s:%d]: unknow order '%s'.\n",
+                               file, linenum, args[1]);
+
+                       return -1;
+               }
+       }
+       else if (!strcmp(args[0], "block") || !strcmp(args[0], "allow") || 
!strcmp(args[0], "deny")) {  /* early blocking based on ACLs */
                int pol = ACL_COND_NONE;
                struct acl_cond *cond;
 
@@ -1134,8 +1150,17 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int inv)
                              file, linenum);
                        return -1;
                }
+
                cond->line = linenum;
-               LIST_ADDQ(&curproxy->block_cond, &cond->list);
+
+               if (!strcmp(args[0], "block"))
+                        Alert("parsing [%s:%d]: '%s' is deprecated, please use 
'deny' instead.\n",
+                               file, linenum, args[0]);
+
+               if (!strcmp(args[0], "allow"))
+                       LIST_ADDQ(&curproxy->allow_cond, &cond->list);
+               else
+                       LIST_ADDQ(&curproxy->deny_cond, &cond->list);
        }
        else if (!strcmp(args[0], "redirect")) {
                int pol = ACL_COND_NONE;
diff --git a/src/haproxy.c b/src/haproxy.c
index abf6eef..e7b8855 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -687,7 +687,13 @@ void deinit(void)
                for (i = 0; i < p->nb_rspadd; i++)
                        free(p->rsp_add[i]);
 
-               list_for_each_entry_safe(cond, condb, &p->block_cond, list) {
+               list_for_each_entry_safe(cond, condb, &p->allow_cond, list) {
+                       LIST_DEL(&cond->list);
+                       prune_acl_cond(cond);
+                       free(cond);
+               }
+
+               list_for_each_entry_safe(cond, condb, &p->deny_cond, list) {
                        LIST_DEL(&cond->list);
                        prune_acl_cond(cond);
                        free(cond);
diff --git a/src/proto_http.c b/src/proto_http.c
index ecbc887..34399aa 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1847,10 +1847,10 @@ int http_process_request(struct session *s, struct 
buffer *req)
         */
 
        do {
-               struct acl_cond *cond;
                struct redirect_rule *rule;
                struct proxy *rule_set = s->be;
                cur_proxy = s->be;
+               int denyreq = 0;
 
                /* first check whether we have some ACLs set to redirect this 
request */
                list_for_each_entry(rule, &cur_proxy->redirect_rules, list) {
@@ -1958,21 +1958,32 @@ int http_process_request(struct session *s, struct 
buffer *req)
                        }
                }
 
-               /* first check whether we have some ACLs set to block this 
request */
-               list_for_each_entry(cond, &cur_proxy->block_cond, list) {
-                       int ret = acl_exec_cond(cond, cur_proxy, s, txn, 
ACL_DIR_REQ);
+               /* check whether we should block this request */
+               if (cur_proxy->order == PR_ORDER_ALLOW_DENY) {
+                       denyreq = 1;
 
-                       ret = acl_pass(ret);
-                       if (cond->pol == ACL_COND_UNLESS)
-                               ret = !ret;
+                       if (check_cond_list(&cur_proxy->allow_cond, cur_proxy, 
s, txn, ACL_DIR_REQ))
+                               denyreq = 0;
 
-                       if (ret) {
-                               txn->status = 403;
-                               /* let's log the request time */
-                               s->logs.tv_request = now;
-                               stream_int_retnclose(req->prod, 
error_message(s, HTTP_ERR_403));
-                               goto return_prx_cond;
-                       }
+                       if (check_cond_list(&cur_proxy->deny_cond, cur_proxy, 
s, txn, ACL_DIR_REQ))
+                               denyreq = 1;
+
+               } else {
+
+                       if (check_cond_list(&cur_proxy->deny_cond, cur_proxy, 
s, txn, ACL_DIR_REQ))
+                               denyreq = 1;
+
+                       if (check_cond_list(&cur_proxy->allow_cond, cur_proxy, 
s, txn, ACL_DIR_REQ))
+                               denyreq = 0;
+
+               }
+
+               if (denyreq) {
+                       txn->status = 403;
+                       /* let's log the request time */
+                       s->logs.tv_request = now;
+                       stream_int_retnclose(req->prod, error_message(s, 
HTTP_ERR_403));
+                       goto return_prx_cond;
                }
 
                /* try headers filters */
-- 
1.6.0.4


Reply via email to