From ff011f26e996c802e69473f462ac13cca1e71656 Mon Sep 17 00:00:00 2001 From: Willy Tarreau <w@1wt.eu> Date: Thu, 6 Jan 2011 17:51:27 +0100 Subject: [PATCH] [REORG] http: move the http-request rules to proto_http And also rename "req_acl_rule" "http_req_rule". At the beginning that was a bit confusing to me, especially the "req_acl" list which in fact holds what we call rules. After some digging, it appeared that some part of the code is 100% HTTP and not just related to authentication anymore, so let's move that part to HTTP and keep the auth-only code in auth.c. --- include/common/uri_auth.h | 4 +- include/proto/auth.h | 2 - include/proto/proto_http.h | 5 +- include/types/auth.h | 19 ------- include/types/proto_http.h | 19 ++++++- include/types/proxy.h | 4 +- src/auth.c | 72 -------------------------- src/cfgparse.c | 41 ++++++++------- src/haproxy.c | 4 +- src/proto_http.c | 102 +++++++++++++++++++++++++++++++------ src/uri_auth.c | 2 +- 11 files changed, 135 insertions(+), 139 deletions(-) diff --git a/include/common/uri_auth.h b/include/common/uri_auth.h index 906cb2c94..495d240d0 100644 --- a/include/common/uri_auth.h +++ b/include/common/uri_auth.h @@ -1,7 +1,7 @@ /* * URI-based user authentication using the HTTP basic method. * - * Copyright 2006-2007 Willy Tarreau <w@1wt.eu> + * Copyright 2006-2011 Willy Tarreau <w@1wt.eu> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,7 +42,7 @@ struct uri_auth { int flags; /* some flags describing the statistics page */ struct stat_scope *scope; /* linked list of authorized proxies */ struct userlist *userlist; /* private userlist to emulate legacy "stats auth user:password" */ - struct list req_acl; /* http stats ACL: allow/deny/auth */ + struct list http_req_rules; /* stats http-request rules : allow/deny/auth */ struct list admin_rules; /* 'stats admin' rules (chained) */ struct uri_auth *next; /* Used at deinit() to build a list of unique elements */ }; diff --git a/include/proto/auth.h b/include/proto/auth.h index 7385ab0d8..8c060b517 100644 --- a/include/proto/auth.h +++ b/include/proto/auth.h @@ -20,9 +20,7 @@ extern struct userlist *userlist; struct userlist *auth_find_userlist(char *name); unsigned int auth_resolve_groups(struct userlist *l, char *groups); -struct req_acl_rule *parse_auth_cond(const char **args, const char *file, int linenum, struct proxy *proxy); void userlist_free(struct userlist *ul); -void req_acl_free(struct list *r); int acl_match_auth(struct acl_test *test, struct acl_pattern *pattern); #endif /* _PROTO_AUTH_H */ diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index a2d91ffe9..73ec35f7e 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -2,7 +2,7 @@ * include/proto/proto_http.h * This file contains HTTP protocol definitions. * - * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -99,6 +99,9 @@ void http_init_txn(struct session *s); void http_end_txn(struct session *s); void http_reset_txn(struct session *s); +struct http_req_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy); +void free_http_req_rules(struct list *r); + /* to be used when contents change in an HTTP message */ #define http_msg_move_end(msg, bytes) do { \ unsigned int _bytes = (bytes); \ diff --git a/include/types/auth.h b/include/types/auth.h index f7e3dd641..e60d363ec 100644 --- a/include/types/auth.h +++ b/include/types/auth.h @@ -22,25 +22,6 @@ #define AU_O_INSECURE 0x00000001 /* insecure, unencrypted password */ -enum { - PR_REQ_ACL_ACT_UNKNOWN = 0, - PR_REQ_ACL_ACT_ALLOW, - PR_REQ_ACL_ACT_DENY, - PR_REQ_ACL_ACT_HTTP_AUTH, - - PR_REQ_ACL_ACT_MAX -}; - - -struct req_acl_rule { - struct list list; - struct acl_cond *cond; /* acl condition to meet */ - unsigned int action; - struct { - char *realm; - } http_auth; -}; - struct auth_users { struct auth_users *next; unsigned int flags; diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 421bb9807..3ccb2bd2d 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -2,7 +2,7 @@ * include/types/proto_http.h * This file contains HTTP protocol definitions. * - * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -228,6 +228,14 @@ enum { HTTP_AUTH_DIGEST, }; +enum { + HTTP_REQ_ACT_UNKNOWN = 0, + HTTP_REQ_ACT_ALLOW, + HTTP_REQ_ACT_DENY, + HTTP_REQ_ACT_HTTP_AUTH, + HTTP_REQ_ACT_MAX +}; + /* This is an HTTP message, as described in RFC2616. It can be either a request * message or a response message. * @@ -285,6 +293,15 @@ struct http_auth_data { char *user, *pass; /* extracted username & password */ }; +struct http_req_rule { + struct list list; + struct acl_cond *cond; /* acl condition to meet */ + unsigned int action; + struct { + char *realm; + } http_auth; +}; + /* This is an HTTP transaction. It contains both a request message and a * response message (which can be empty). */ diff --git a/include/types/proxy.h b/include/types/proxy.h index 6208d7ba4..214cdf554 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -2,7 +2,7 @@ * include/types/proxy.h * This file defines everything related to proxies. * - * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -188,7 +188,7 @@ struct proxy { char *name; /* default backend name during config parse */ } defbe; struct list acl; /* ACL declared on this proxy */ - struct list req_acl; /* request ACL: allow/deny/http-auth */ + struct list http_req_rules; /* HTTP request rules: allow/deny/http-auth */ struct list block_cond; /* early blocking conditions (chained) */ struct list redirect_rules; /* content redirecting rules (chained) */ struct list switching_rules; /* content switching rules (chained) */ diff --git a/src/auth.c b/src/auth.c index 8ae485864..fd4e063ec 100644 --- a/src/auth.c +++ b/src/auth.c @@ -85,65 +85,6 @@ auth_resolve_groups(struct userlist *l, char *groups) return group_mask; } -struct req_acl_rule * -parse_auth_cond(const char **args, const char *file, int linenum, struct proxy *proxy) -{ - struct req_acl_rule *req_acl; - int cur_arg; - - req_acl = (struct req_acl_rule*)calloc(1, sizeof(struct req_acl_rule)); - if (!req_acl) { - Alert("parsing [%s:%d]: out of memory.\n", file, linenum); - return NULL; - } - - if (!*args[0]) { - goto req_error_parsing; - } else if (!strcmp(args[0], "allow")) { - req_acl->action = PR_REQ_ACL_ACT_ALLOW; - cur_arg = 1; - } else if (!strcmp(args[0], "deny")) { - req_acl->action = PR_REQ_ACL_ACT_DENY; - cur_arg = 1; - } else if (!strcmp(args[0], "auth")) { - req_acl->action = PR_REQ_ACL_ACT_HTTP_AUTH; - cur_arg = 1; - - while(*args[cur_arg]) { - if (!strcmp(args[cur_arg], "realm")) { - req_acl->http_auth.realm = strdup(args[cur_arg + 1]); - cur_arg+=2; - continue; - } else - break; - } - } else { -req_error_parsing: - Alert("parsing [%s:%d]: %s '%s', expects 'allow', 'deny', 'auth'.\n", - file, linenum, *args[1]?"unknown parameter":"missing keyword in", args[*args[1]?1:0]); - return NULL; - } - - if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { - struct acl_cond *cond; - - if ((cond = build_acl_cond(file, linenum, proxy, args+cur_arg)) == NULL) { - Alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition.\n", - file, linenum, args[0]); - return NULL; - } - req_acl->cond = cond; - } - else if (*args[cur_arg]) { - Alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or" - " either 'if' or 'unless' followed by a condition but found '%s'.\n", - file, linenum, args[0], args[cur_arg]); - return NULL; - } - - return req_acl; -} - void userlist_free(struct userlist *ul) { @@ -172,19 +113,6 @@ userlist_free(struct userlist *ul) }; } -void -req_acl_free(struct list *r) { - struct req_acl_rule *tr, *pr; - - list_for_each_entry_safe(pr, tr, r, list) { - LIST_DEL(&pr->list); - if (pr->action == PR_REQ_ACL_ACT_HTTP_AUTH) - free(pr->http_auth.realm); - - free(pr); - } -} - /* * Authenticate and authorize user; return 1 if OK, 0 if case of error. */ diff --git a/src/cfgparse.c b/src/cfgparse.c index aeec87ba2..b5e077af7 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1,7 +1,7 @@ /* * Configuration parser * - * Copyright 2000-2010 Willy Tarreau <w@1wt.eu> + * Copyright 2000-2011 Willy Tarreau <w@1wt.eu> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1058,7 +1058,7 @@ static void init_new_proxy(struct proxy *p) memset(p, 0, sizeof(struct proxy)); LIST_INIT(&p->pendconns); LIST_INIT(&p->acl); - LIST_INIT(&p->req_acl); + LIST_INIT(&p->http_req_rules); LIST_INIT(&p->block_cond); LIST_INIT(&p->redirect_rules); LIST_INIT(&p->mon_fail_cond); @@ -2462,7 +2462,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->conn_retries = atol(args[1]); } else if (!strcmp(args[0], "http-request")) { /* request access control: allow/deny/auth */ - struct req_acl_rule *req_acl; + struct http_req_rule *rule; if (curproxy == &defproxy) { Alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); @@ -2470,22 +2470,21 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } - - if (!LIST_ISEMPTY(&curproxy->req_acl) && !LIST_PREV(&curproxy->req_acl, struct req_acl_rule *, list)->cond) { + if (!LIST_ISEMPTY(&curproxy->http_req_rules) && !LIST_PREV(&curproxy->http_req_rules, struct http_req_rule *, list)->cond) { Warning("parsing [%s:%d]: previous '%s' action has no condition attached, further entries are NOOP.\n", file, linenum, args[0]); err_code |= ERR_WARN; } - req_acl = parse_auth_cond((const char **)args + 1, file, linenum, curproxy); + rule = parse_http_req_cond((const char **)args + 1, file, linenum, curproxy); - if (!req_acl) { + if (!rule) { err_code |= ERR_ALERT | ERR_ABORT; goto out; } - err_code |= warnif_cond_requires_resp(req_acl->cond, file, linenum); - LIST_ADDQ(&curproxy->req_acl, &req_acl->list); + err_code |= warnif_cond_requires_resp(rule->cond, file, linenum); + LIST_ADDQ(&curproxy->http_req_rules, &rule->list); } else if (!strcmp(args[0], "block")) { /* early blocking based on ACLs */ if (curproxy == &defproxy) { @@ -3073,7 +3072,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } } else if (!strcmp(args[1], "http-request")) { /* request access control: allow/deny/auth */ - struct req_acl_rule *req_acl; + struct http_req_rule *rule; if (curproxy == &defproxy) { Alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); @@ -3087,22 +3086,22 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } - if (!LIST_ISEMPTY(&curproxy->uri_auth->req_acl) && - !LIST_PREV(&curproxy->uri_auth->req_acl, struct req_acl_rule *, list)->cond) { + if (!LIST_ISEMPTY(&curproxy->uri_auth->http_req_rules) && + !LIST_PREV(&curproxy->uri_auth->http_req_rules, struct http_req_rule *, list)->cond) { Warning("parsing [%s:%d]: previous '%s' action has no condition attached, further entries are NOOP.\n", file, linenum, args[0]); err_code |= ERR_WARN; } - req_acl = parse_auth_cond((const char **)args + 2, file, linenum, curproxy); + rule = parse_http_req_cond((const char **)args + 2, file, linenum, curproxy); - if (!req_acl) { + if (!rule) { err_code |= ERR_ALERT | ERR_ABORT; goto out; } - err_code |= warnif_cond_requires_resp(req_acl->cond, file, linenum); - LIST_ADDQ(&curproxy->uri_auth->req_acl, &req_acl->list); + err_code |= warnif_cond_requires_resp(rule->cond, file, linenum); + LIST_ADDQ(&curproxy->uri_auth->http_req_rules, &rule->list); } else if (!strcmp(args[1], "auth")) { if (*(args[2]) == 0) { @@ -5935,7 +5934,7 @@ int check_config_validity() } if (curproxy->uri_auth && !(curproxy->uri_auth->flags & ST_CONVDONE) && - !LIST_ISEMPTY(&curproxy->uri_auth->req_acl) && + !LIST_ISEMPTY(&curproxy->uri_auth->http_req_rules) && (curproxy->uri_auth->userlist || curproxy->uri_auth->auth_realm )) { Alert("%s '%s': stats 'auth'/'realm' and 'http-request' can't be used at the same time.\n", "proxy", curproxy->id); @@ -5945,7 +5944,7 @@ int check_config_validity() if (curproxy->uri_auth && curproxy->uri_auth->userlist && !(curproxy->uri_auth->flags & ST_CONVDONE)) { const char *uri_auth_compat_req[10]; - struct req_acl_rule *req_acl; + struct http_req_rule *rule; int i = 0; /* build the ACL condition from scratch. We're relying on anonymous ACLs for that */ @@ -5962,13 +5961,13 @@ int check_config_validity() uri_auth_compat_req[i++] = "}"; uri_auth_compat_req[i++] = ""; - req_acl = parse_auth_cond(uri_auth_compat_req, "internal-stats-auth-compat", 0, curproxy); - if (!req_acl) { + rule = parse_http_req_cond(uri_auth_compat_req, "internal-stats-auth-compat", 0, curproxy); + if (!rule) { cfgerr++; break; } - LIST_ADDQ(&curproxy->uri_auth->req_acl, &req_acl->list); + LIST_ADDQ(&curproxy->uri_auth->http_req_rules, &rule->list); if (curproxy->uri_auth->auth_realm) { free(curproxy->uri_auth->auth_realm); diff --git a/src/haproxy.c b/src/haproxy.c index 064c49c47..915f47a73 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -840,7 +840,7 @@ void deinit(void) free(p->desc); free(p->fwdfor_hdr_name); - req_acl_free(&p->req_acl); + free_http_req_rules(&p->http_req_rules); pool_destroy2(p->req_cap_pool); pool_destroy2(p->rsp_cap_pool); @@ -861,7 +861,7 @@ void deinit(void) free(uap->desc); userlist_free(uap->userlist); - req_acl_free(&uap->req_acl); + free_http_req_rules(&uap->http_req_rules); free(uap); } diff --git a/src/proto_http.c b/src/proto_http.c index 348c3bc10..156fd4b74 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2983,31 +2983,31 @@ int http_process_req_stat_post(struct stream_interface *si, struct http_txn *txn /* returns a pointer to the first rule which forbids access (deny or http_auth), * or NULL if everything's OK. */ -static inline struct req_acl_rule * +static inline struct http_req_rule * http_check_access_rule(struct proxy *px, struct list *rules, struct session *s, struct http_txn *txn) { - struct req_acl_rule *req_acl; + struct http_req_rule *rule; - list_for_each_entry(req_acl, rules, list) { + list_for_each_entry(rule, rules, list) { int ret = 1; - if (req_acl->action >= PR_REQ_ACL_ACT_MAX) + if (rule->action >= HTTP_REQ_ACT_MAX) continue; /* check condition, but only if attached */ - if (req_acl->cond) { - ret = acl_exec_cond(req_acl->cond, px, s, txn, ACL_DIR_REQ); + if (rule->cond) { + ret = acl_exec_cond(rule->cond, px, s, txn, ACL_DIR_REQ); ret = acl_pass(ret); - if (req_acl->cond->pol == ACL_COND_UNLESS) + if (rule->cond->pol == ACL_COND_UNLESS) ret = !ret; } if (ret) { - if (req_acl->action == PR_REQ_ACL_ACT_ALLOW) + if (rule->action == HTTP_REQ_ACT_ALLOW) return NULL; /* no problem */ else - return req_acl; /* most likely a deny or auth rule */ + return rule; /* most likely a deny or auth rule */ } } return NULL; @@ -3025,7 +3025,7 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s struct http_txn *txn = &s->txn; struct http_msg *msg = &txn->req; struct acl_cond *cond; - struct req_acl_rule *req_acl_final = NULL; + struct http_req_rule *http_req_last_rule = NULL; struct redirect_rule *rule; struct cond_wordlist *wl; int del_ka, del_cl, do_stats; @@ -3067,19 +3067,19 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s } /* evaluate http-request rules */ - req_acl_final = http_check_access_rule(px, &px->req_acl, s, txn); + http_req_last_rule = http_check_access_rule(px, &px->http_req_rules, s, txn); /* evaluate stats http-request rules only if http-request is OK */ - if (!req_acl_final) { + if (!http_req_last_rule) { do_stats = stats_check_uri(s->rep->prod, txn, px); if (do_stats) - req_acl_final = http_check_access_rule(px, &px->uri_auth->req_acl, s, txn); + http_req_last_rule = http_check_access_rule(px, &px->uri_auth->http_req_rules, s, txn); } else do_stats = 0; /* return a 403 if either rule has blocked */ - if (req_acl_final && req_acl_final->action == PR_REQ_ACL_ACT_DENY) { + if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_DENY) { txn->status = 403; s->logs.tv_request = now; stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403)); @@ -3180,9 +3180,9 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s /* we can be blocked here because the request needs to be authenticated, * either to pass or to access stats. */ - if (req_acl_final && req_acl_final->action == PR_REQ_ACL_ACT_HTTP_AUTH) { + if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_HTTP_AUTH) { struct chunk msg; - char *realm = req_acl_final->http_auth.realm; + char *realm = http_req_last_rule->http_auth.realm; if (!realm) realm = do_stats?STATS_DEFAULT_REALM:px->id; @@ -7609,6 +7609,76 @@ void http_reset_txn(struct session *s) s->rep->analyse_exp = TICK_ETERNITY; } +void free_http_req_rules(struct list *r) { + struct http_req_rule *tr, *pr; + + list_for_each_entry_safe(pr, tr, r, list) { + LIST_DEL(&pr->list); + if (pr->action == HTTP_REQ_ACT_HTTP_AUTH) + free(pr->http_auth.realm); + + free(pr); + } +} + +struct http_req_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy) +{ + struct http_req_rule *rule; + int cur_arg; + + rule = (struct http_req_rule*)calloc(1, sizeof(struct http_req_rule)); + if (!rule) { + Alert("parsing [%s:%d]: out of memory.\n", file, linenum); + return NULL; + } + + if (!*args[0]) { + goto req_error_parsing; + } else if (!strcmp(args[0], "allow")) { + rule->action = HTTP_REQ_ACT_ALLOW; + cur_arg = 1; + } else if (!strcmp(args[0], "deny")) { + rule->action = HTTP_REQ_ACT_DENY; + cur_arg = 1; + } else if (!strcmp(args[0], "auth")) { + rule->action = HTTP_REQ_ACT_HTTP_AUTH; + cur_arg = 1; + + while(*args[cur_arg]) { + if (!strcmp(args[cur_arg], "realm")) { + rule->http_auth.realm = strdup(args[cur_arg + 1]); + cur_arg+=2; + continue; + } else + break; + } + } else { +req_error_parsing: + Alert("parsing [%s:%d]: %s '%s', expects 'allow', 'deny', 'auth'.\n", + file, linenum, *args[1]?"unknown parameter":"missing keyword in", args[*args[1]?1:0]); + return NULL; + } + + if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { + struct acl_cond *cond; + + if ((cond = build_acl_cond(file, linenum, proxy, args+cur_arg)) == NULL) { + Alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition.\n", + file, linenum, args[0]); + return NULL; + } + rule->cond = cond; + } + else if (*args[cur_arg]) { + Alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or" + " either 'if' or 'unless' followed by a condition but found '%s'.\n", + file, linenum, args[0], args[cur_arg]); + return NULL; + } + + return rule; +} + /************************************************************************/ /* The code below is dedicated to ACL parsing and matching */ /************************************************************************/ diff --git a/src/uri_auth.c b/src/uri_auth.c index fdbcef001..5a9284931 100644 --- a/src/uri_auth.c +++ b/src/uri_auth.c @@ -31,7 +31,7 @@ struct uri_auth *stats_check_init_uri_auth(struct uri_auth **root) if ((u = (struct uri_auth *)calloc(1, sizeof (*u))) == NULL) goto out_u; - LIST_INIT(&u->req_acl); + LIST_INIT(&u->http_req_rules); LIST_INIT(&u->admin_rules); } else u = *root;