MINOR: ssl: allow to change the signature algorithm for client authentication

This commit introduces the keyword "client-sigalgs" for the bind line,
which does the same as "sigalgs" but for the client authentication.

"ssl-default-bind-client-sigalgs" allows to set the default parameter
for all the bind lines.

This patch should fix issue #2081.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 0111b61..7926b72 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1095,6 +1095,7 @@
    - setenv
    - ssl-default-bind-ciphers
    - ssl-default-bind-ciphersuites
+   - ssl-default-bind-client-sigalgs
    - ssl-default-bind-curves
    - ssl-default-bind-options
    - ssl-default-bind-sigalgs
@@ -2168,6 +2169,21 @@
   "ssl-default-bind-ciphers" keyword. Please check the "bind" keyword for more
   information.
 
+ssl-default-bind-client-sigalgs <sigalgs>
+  This setting is only available when support for OpenSSL was built in. It sets
+  the default string describing the list of signature algorithms related to
+  client authentication for all "bind" lines which do not explicitly define
+  theirs. The format of the string is a colon-delimited list of signature
+  algorithms. Each signature algorithm can use one of two forms: TLS1.3 signature
+  scheme names ("rsa_pss_rsae_sha256") or the public key algorithm + digest form
+  ("ECDSA+SHA256"). A list can contain both forms. For more information on the
+  format, see SSL_CTX_set1_client_sigalgs(3). A list of signature algorithms is
+  also available in RFC8446 section 4.2.3 and in OpenSSL in the ssl/t1_lib.c
+  file.  This setting is not applicable to TLSv1.1 and earlier versions of the
+  protocol as the signature algorithms aren't separately negotiated in these
+  versions. It is not recommended to change this setting unless compatibility
+  with a middlebox is required.
+
 ssl-default-bind-curves <curves>
   This setting is only available when support for OpenSSL was built in. It sets
   the default string describing the list of elliptic curves algorithms ("curve
@@ -14809,6 +14825,13 @@
   OpenSSL man pages under the "ciphersuites" section. For cipher configuration
   for TLSv1.2 and earlier, please check the "ciphers" keyword.
 
+client-sigalgs <sigalgs>
+  This setting is only available when support for OpenSSL was built in. It sets
+  the string describing the list of signature algorithms related to client
+  authentication that are negotiated . The format of the string is defined in
+  "man 3 SSL_CTX_set1_client_sigalgs" from the OpenSSL man pages. It is not
+  recommended to use this setting if no specific usecase was identified.
+
 crl-file <crlfile>
   This setting is only available when support for OpenSSL was built in. It
   designates a PEM file from which to load certificate revocation list used
diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h
index ff5b30d..7384739 100644
--- a/include/haproxy/listener-t.h
+++ b/include/haproxy/listener-t.h
@@ -144,6 +144,7 @@
 	char *curves;	           /* curves suite to use for ECDHE */
 	char *ecdhe;               /* named curve to use for ECDHE */
 	char *sigalgs;             /* Signature algorithms */
+	char *client_sigalgs;      /* Client Signature algorithms */
 	struct tls_version_filter ssl_methods_cfg; /* original ssl methods found in configuration */
 	struct tls_version_filter ssl_methods; /* actual ssl methods used at runtime */
 #endif
diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h
index b4d3502..0b94a0e 100644
--- a/include/haproxy/ssl_sock-t.h
+++ b/include/haproxy/ssl_sock-t.h
@@ -282,6 +282,9 @@
 #if defined(SSL_CTX_set1_sigalgs_list)
 	char *listen_default_sigalgs;
 #endif
+#if defined(SSL_CTX_set1_sigalgs_list)
+	char *listen_default_client_sigalgs;
+#endif
 	int listen_default_ssloptions;
 	int connect_default_ssloptions;
 	struct tls_version_filter listen_default_sslmethods;
diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c
index 48e1450..42d7f40 100644
--- a/src/cfgparse-ssl.c
+++ b/src/cfgparse-ssl.c
@@ -344,6 +344,33 @@
 }
 #endif
 
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+/*
+ * parse the "ssl-default-bind-client-sigalgs" keyword in a global section.
+ * Returns <0 on alert, >0 on warning, 0 on success.
+ */
+static int ssl_parse_global_client_sigalgs(char **args, int section_type, struct proxy *curpx,
+                                   const struct proxy *defpx, const char *file, int line,
+				   char **err)
+{
+	char **target;
+
+	target = &global_ssl.listen_default_client_sigalgs;
+
+	if (too_many_args(1, args, err, NULL))
+		return -1;
+
+	if (*(args[1]) == 0) {
+		memprintf(err, "global statement '%s' expects signature algorithms as an arguments.", args[0]);
+		return -1;
+	}
+
+	free(*target);
+	*target = strdup(args[1]);
+	return 0;
+}
+#endif
+
 /* parse various global tune.ssl settings consisting in positive integers.
  * Returns <0 on alert, >0 on warning, 0 on success.
  */
@@ -846,6 +873,26 @@
 	return ssl_bind_parse_sigalgs(args, cur_arg, px, &conf->ssl_conf, 0, err);
 }
 
+/* parse the "client-sigalgs" bind keyword */
+static int ssl_bind_parse_client_sigalgs(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, int from_cli, char **err)
+{
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+	if (!*args[cur_arg + 1]) {
+		memprintf(err, "'%s' : missing signature algorithm list", args[cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+	conf->client_sigalgs = strdup(args[cur_arg + 1]);
+	return 0;
+#else
+	memprintf(err, "'%s' : library does not support setting signature algorithms", args[cur_arg]);
+	return ERR_ALERT | ERR_FATAL;
+#endif
+}
+static int bind_parse_client_sigalgs(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+	return ssl_bind_parse_client_sigalgs(args, cur_arg, px, &conf->ssl_conf, 0, err);
+}
+
 
 /* parse the "ecdhe" bind keyword keyword */
 static int ssl_bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, int from_cli, char **err)
@@ -2056,6 +2103,7 @@
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
 	{ "ciphersuites",          ssl_bind_parse_ciphersuites,     1 }, /* set TLS 1.3 cipher suite */
 #endif
+	{ "client-sigalgs",        ssl_bind_parse_client_sigalgs,     1 }, /* set SSL client signature algorithms */
 	{ "crl-file",              ssl_bind_parse_crl_file,         1 }, /* set certificate revocation list file use on client cert verify */
 	{ "curves",                ssl_bind_parse_curves,           1 }, /* set SSL curve suite */
 	{ "ecdhe",                 ssl_bind_parse_ecdhe,            1 }, /* defines named curve for elliptic curve Diffie-Hellman */
@@ -2084,6 +2132,7 @@
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
 	{ "ciphersuites",          bind_parse_ciphersuites,       1 }, /* set TLS 1.3 cipher suite */
 #endif
+	{ "client-sigalgs",        bind_parse_client_sigalgs,     1 }, /* set SSL client signature algorithms */
 	{ "crl-file",              bind_parse_crl_file,           1 }, /* set certificate revocation list file use on client cert verify */
 	{ "crt",                   bind_parse_crt,                1 }, /* load SSL certificates from this location */
 	{ "crt-ignore-err",        bind_parse_ignore_err,         1 }, /* set error IDs to ignore on verify depth == 0 */
@@ -2210,6 +2259,9 @@
 #if defined(SSL_CTX_set1_sigalgs_list)
 	{ CFG_GLOBAL, "ssl-default-bind-sigalgs", ssl_parse_global_sigalgs },
 #endif
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+	{ CFG_GLOBAL, "ssl-default-bind-client-sigalgs", ssl_parse_global_client_sigalgs },
+#endif
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
 	{ CFG_GLOBAL, "ssl-default-bind-ciphersuites", ssl_parse_global_ciphersuites },
 	{ CFG_GLOBAL, "ssl-default-server-ciphersuites", ssl_parse_global_ciphersuites },
diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c
index 3d3ae40..9ea5ea0 100644
--- a/src/ssl_crtlist.c
+++ b/src/ssl_crtlist.c
@@ -77,6 +77,9 @@
 #if defined(SSL_CTX_set1_sigalgs_list)
 		ha_free(&conf->sigalgs);
 #endif
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+		ha_free(&conf->client_sigalgs);
+#endif
 	}
 }
 
@@ -161,6 +164,13 @@
 			goto error;
 	}
 #endif
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+	if (src->client_sigalgs) {
+		dst->client_sigalgs = strdup(src->client_sigalgs);
+		if (!dst->client_sigalgs)
+			goto error;
+	}
+#endif
 	return dst;
 
 error:
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index b44b5ce..79ac2d6 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -4573,6 +4573,9 @@
 #if defined(SSL_CTX_set1_sigalgs_list)
 	const char *conf_sigalgs = NULL;
 #endif
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+	const char *conf_client_sigalgs = NULL;
+#endif
 
 	if (ssl_conf) {
 		struct tls_version_filter *conf_ssl_methods = &ssl_conf->ssl_methods;
@@ -4786,6 +4789,17 @@
 	}
 #endif
 
+#if defined(SSL_CTX_set1_client_sigalgs_list)
+	conf_client_sigalgs = (ssl_conf && ssl_conf->client_sigalgs) ? ssl_conf->client_sigalgs : bind_conf->ssl_conf.client_sigalgs;
+	if (conf_client_sigalgs) {
+		if (!SSL_CTX_set1_client_sigalgs_list(ctx, conf_client_sigalgs)) {
+			memprintf(err, "%sProxy '%s': unable to set SSL Signature Algorithm list to '%s' for bind '%s' at [%s:%d].\n",
+			          err && *err ? *err : "", curproxy->id, conf_client_sigalgs, bind_conf->arg, bind_conf->file, bind_conf->line);
+			cfgerr |= ERR_ALERT | ERR_FATAL;
+		}
+	}
+#endif
+
 	return cfgerr;
 }