]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: http-rules: Use an action function to eval http-request auth rules
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 27 May 2020 13:26:43 +0000 (15:26 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 28 May 2020 13:07:20 +0000 (15:07 +0200)
Now http-request auth rules are evaluated in a dedicated function and no longer
handled "in place" during the HTTP rules evaluation. Thus the action name
ACT_HTTP_REQ_AUTH is removed. In additionn, http_reply_40x_unauthorized() is
also removed. This part is now handled in the new action_ptr callback function.

include/types/action.h
src/http_act.c
src/http_ana.c

index b01380c3ab678ffea5d1544ac04e5126b7a1490d..00c37fa6a2d13a5b18ce24ad151a1555ff730733 100644 (file)
@@ -89,7 +89,6 @@ enum act_name {
 
        /* http request actions. */
        ACT_HTTP_REQ_TARPIT,
-       ACT_HTTP_REQ_AUTH,
 
        /* tcp actions */
        ACT_TCP_EXPECT_PX,
index ab4a8bb94a817b2b020e7c17d8b9c68d2d34c7a0..b18da6164ef0eb59ffb3cee46a39415ed38ab5b7 100644 (file)
@@ -25,6 +25,7 @@
 #include <common/initcall.h>
 #include <common/memory.h>
 #include <common/standard.h>
+#include <common/uri_auth.h>
 #include <common/version.h>
 
 #include <types/capture.h>
@@ -884,6 +885,85 @@ static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, stru
        return ACT_RET_PRS_OK;
 }
 
+
+/* This function executes a auth action. It builds an 401/407 HTX message using
+ * the corresponding proxy's error message. On success, it returns
+ * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
+ */
+static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
+                                       struct session *sess, struct stream *s, int flags)
+{
+       struct channel *req = &s->req;
+       struct channel *res = &s->res;
+       struct htx *htx = htx_from_buf(&res->buf);
+       struct http_reply *reply;
+       const char *auth_realm;
+       struct http_hdr_ctx ctx;
+       struct ist hdr;
+
+       /* Auth might be performed on regular http-req rules as well as on stats */
+       auth_realm = rule->arg.http.str.ptr;
+       if (!auth_realm) {
+               if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
+                       auth_realm = STATS_DEFAULT_REALM;
+               else
+                       auth_realm = px->id;
+       }
+
+       if (!(s->txn->flags & TX_USE_PX_CONN)) {
+               s->txn->status = 401;
+               hdr = ist("WWW-Authenticate");
+       }
+       else {
+               s->txn->status = 407;
+               hdr = ist("Proxy-Authenticate");
+       }
+       reply = http_error_message(s);
+       channel_htx_truncate(res, htx);
+
+       if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
+               goto fail;
+
+       /* Write the generic 40x message */
+       if (http_reply_to_htx(s, htx, reply) == -1)
+               goto fail;
+
+       /* Remove all existing occurrences of the XXX-Authenticate header */
+       ctx.blk = NULL;
+       while (http_find_header(htx, hdr, &ctx, 1))
+               http_remove_header(htx, &ctx);
+
+       /* Now a the right XXX-Authenticate header */
+       if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
+               goto fail;
+
+       /* Finally forward the reply */
+       htx_to_buf(htx, &res->buf);
+       if (!http_forward_proxy_resp(s, 1))
+               goto fail;
+
+       /* Note: Only eval on the request */
+       s->logs.tv_request = now;
+       req->analysers &= AN_REQ_FLT_END;
+
+       if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
+               _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
+
+       if (!(s->flags & SF_ERR_MASK))
+               s->flags |= SF_ERR_LOCAL;
+       if (!(s->flags & SF_FINST_MASK))
+               s->flags |= SF_FINST_R;
+
+       stream_inc_http_err_ctr(s);
+       return ACT_RET_ABRT;
+
+  fail:
+       /* If an error occurred, remove the incomplete HTTP response from the
+        * buffer */
+       channel_htx_truncate(res, htx);
+       return ACT_RET_ERR;
+}
+
 /* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
  * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
  */
@@ -892,8 +972,9 @@ static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, stru
 {
        int cur_arg;
 
-       rule->action = ACT_HTTP_REQ_AUTH;
+       rule->action = ACT_CUSTOM;
        rule->flags |= ACT_FLAG_FINAL;
+       rule->action_ptr = http_action_auth;
        rule->release_ptr = release_http_action;
 
        cur_arg = *orig_arg;
index e239f7c31343f7b66161f195e66a16e0df325c21..cf9570c3b7dc8557adc4edffa730ce86ff18e0f6 100644 (file)
@@ -63,7 +63,6 @@ static int http_handle_stats(struct stream *s, struct channel *req);
 
 static int http_handle_expect_hdr(struct stream *s, struct htx *htx, struct http_msg *msg);
 static int http_reply_100_continue(struct stream *s);
-static int http_reply_40x_unauthorized(struct stream *s, const char *auth_realm);
 
 /* This stream analyser waits for a complete HTTP request. It returns 1 if the
  * processing can continue on next analysers, or zero if it either needs more
@@ -2824,7 +2823,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
        struct htx *htx;
        struct act_rule *rule;
        struct http_hdr_ctx ctx;
-       const char *auth_realm;
        enum rule_result rule_ret = HTTP_RULE_RES_CONT;
        int act_opts = 0;
 
@@ -2920,25 +2918,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
                                rule_ret = HTTP_RULE_RES_DENY;
                                goto end;
 
-                       case ACT_HTTP_REQ_AUTH:
-                               /* Auth might be performed on regular http-req rules as well as on stats */
-                               auth_realm = rule->arg.http.str.ptr;
-                               if (!auth_realm) {
-                                       if (px->uri_auth && rules == &px->uri_auth->http_req_rules)
-                                               auth_realm = STATS_DEFAULT_REALM;
-                                       else
-                                               auth_realm = px->id;
-                               }
-                               /* send 401/407 depending on whether we use a proxy or not. We still
-                                * count one error, because normal browsing won't significantly
-                                * increase the counter but brute force attempts will.
-                                */
-                               rule_ret = HTTP_RULE_RES_ABRT;
-                               if (http_reply_40x_unauthorized(s, auth_realm) == -1)
-                                       rule_ret = HTTP_RULE_RES_ERROR;
-                               stream_inc_http_err_ctr(s);
-                               goto end;
-
                        case ACT_HTTP_REDIR:
                                rule_ret = HTTP_RULE_RES_ABRT;
                                if (!http_apply_redirect_rule(rule->arg.redir, s, txn))
@@ -4902,71 +4881,6 @@ static int http_reply_100_continue(struct stream *s)
 }
 
 
-/* Send a 401-Unauthorized or 407-Unauthorized response to the client, depending
- * ont whether we use a proxy or not. It returns 0 on success and -1 on
- * error. The response channel is updated accordingly.
- */
-static int http_reply_40x_unauthorized(struct stream *s, const char *auth_realm)
-{
-       struct channel *res = &s->res;
-       struct htx *htx = htx_from_buf(&res->buf);
-       struct http_reply *reply;
-       struct http_hdr_ctx ctx;
-       struct ist hdr;
-
-       if (!(s->txn->flags & TX_USE_PX_CONN)) {
-               s->txn->status = 401;
-               hdr = ist("WWW-Authenticate");
-       }
-       else {
-               s->txn->status = 407;
-               hdr = ist("Proxy-Authenticate");
-       }
-       reply = http_error_message(s);
-       channel_htx_truncate(res, htx);
-
-       if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
-               goto fail;
-
-       /* Write the generic 40x message */
-       if (http_reply_to_htx(s, htx, reply) == -1)
-               goto fail;
-
-       /* Remove all existing occurrences of the XXX-Authenticate header */
-       ctx.blk = NULL;
-       while (http_find_header(htx, hdr, &ctx, 1))
-               http_remove_header(htx, &ctx);
-
-       /* Now a the right XXX-Authenticate header */
-       if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
-               goto fail;
-
-       /* Finally forward the reply */
-       htx_to_buf(htx, &res->buf);
-       if (!http_forward_proxy_resp(s, 1))
-               goto fail;
-
-       /* Note: Only eval on the request */
-       s->logs.tv_request = now;
-       s->req.analysers &= AN_REQ_FLT_END;
-
-       if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
-               _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
-
-       if (!(s->flags & SF_ERR_MASK))
-               s->flags |= SF_ERR_LOCAL;
-       if (!(s->flags & SF_FINST_MASK))
-               s->flags |= SF_FINST_R;
-
-       return 0;
-
-  fail:
-       /* If an error occurred, remove the incomplete HTTP response from the
-        * buffer */
-       channel_htx_truncate(res, htx);
-       return -1;
-}
-
 /*
  * Capture headers from message <htx> according to header list <cap_hdr>, and
  * fill the <cap> pointers appropriately.