MEDIUM: http-ana: Add a proxy option to restrict chars in request header names

The "http-restrict-req-hdr-names" option can now be set to restrict allowed
characters in the request header names to the "[a-zA-Z0-9-]" charset.

Idea of this option is to not send header names with non-alphanumeric or
hyphen character. It is especially important for FastCGI application because
all those characters are converted to underscore. For instance,
"X-Forwarded-For" and "X_Forwarded_For" are both converted to
"HTTP_X_FORWARDED_FOR". So, header names can be mixed up by FastCGI
applications. And some HAProxy rules may be bypassed by mangling header
names. In addition, some non-HTTP compliant servers may incorrectly handle
requests when header names contain characters ouside the "[a-zA-Z0-9-]"
charset.

When this option is set, the policy must be specify:

  * preserve: It disables the filtering. It is the default mode for HTTP
              proxies with no FastCGI application configured.

  * delete: It removes request headers with a name containing a character
            outside the "[a-zA-Z0-9-]" charset. It is the default mode for
            HTTP backends with a configured FastCGI application.

  * reject: It rejects the request with a 403-Forbidden response if it
            contains a header name with a character outside the
            "[a-zA-Z0-9-]" charset.

The option is evaluated per-proxy and after http-request rules evaluation.

This patch may be backported to avoid any secuirty issue with FastCGI
application (so as far as 2.2).
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index d890295..e7cd651 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -2459,6 +2459,38 @@
 				}
 			} /* end while loop */
 		}
+		else if (strcmp(args[1], "http-restrict-req-hdr-names") == 0) {
+			if (kwm != KWM_STD) {
+				ha_alert("parsing [%s:%d]: negation/default is not supported for option '%s'.\n",
+					 file, linenum, args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			if (alertif_too_many_args(2, file, linenum, args, &err_code))
+				goto out;
+
+			if (*(args[2]) == 0) {
+				ha_alert("parsing [%s:%d] : missing parameter. option '%s' expects 'preserve', 'reject' or 'delete' option.\n",
+					 file, linenum, args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			curproxy->options2 &= ~PR_O2_RSTRICT_REQ_HDR_NAMES_MASK;
+			if (strcmp(args[2], "preserve") == 0)
+				curproxy->options2 |= PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP;
+			else if (strcmp(args[2], "reject") == 0)
+				curproxy->options2 |= PR_O2_RSTRICT_REQ_HDR_NAMES_BLK;
+			else if (strcmp(args[2], "delete") == 0)
+				curproxy->options2 |= PR_O2_RSTRICT_REQ_HDR_NAMES_DEL;
+			else {
+				ha_alert("parsing [%s:%d] : invalid parameter '%s'. option '%s' expects 'preserve', 'reject' or 'delete' option.\n",
+					 file, linenum, args[2], args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+		}
 		else {
 			const char *best = proxy_find_best_option(args[1], common_options);