BUG/MEDIUM: ssl: Verify error codes can exceed 63

The CRT and CA verify error codes were stored in 6 bits each in the
xprt_st field of the ssl_sock_ctx meaning that only error code up to 63
could be stored. Likewise, the ca-ignore-err and crt-ignore-err options
relied on two unsigned long longs that were used as bitfields for all
the ignored error codes. On the latest OpenSSL1.1.1 and with OpenSSLv3
and newer, verify errors have exceeded this value so these two storages
must be increased. The error codes will now be stored on 7 bits each and
the ignore-err bitfields are replaced by a big enough array and
dedicated bit get and set functions.

It can be backported on all stable branches.

[wla: let it be tested a little while before backport]
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
(cherry picked from commit 9b25982716f0416c28f8fc894c58eb40885cf9e5)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
(cherry picked from commit 64fa46abccf9f9599b575ba57ea4786c53fae9df)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 5653517c2eee4c97a545d3122d8ca84a045e819b)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h
index c350290..476e65a 100644
--- a/include/haproxy/listener-t.h
+++ b/include/haproxy/listener-t.h
@@ -156,12 +156,21 @@
 #endif
 };
 
+/*
+ * In OpenSSL 3.0.0, the biggest verify error code's value is 94 and on the
+ * latest 1.1.1 it already reaches 79 so we need to size the ca/crt-ignore-err
+ * arrays accordingly. If the max error code increases, the arrays might need to
+ * be resized.
+ */
+#define SSL_MAX_VFY_ERROR_CODE 94
+#define IGNERR_BF_SIZE ((SSL_MAX_VFY_ERROR_CODE >> 6) + 1)
+
 /* "bind" line settings */
 struct bind_conf {
 #ifdef USE_OPENSSL
 	struct ssl_bind_conf ssl_conf; /* ssl conf for ctx setting */
-	unsigned long long ca_ignerr;  /* ignored verify errors in handshake if depth > 0 */
-	unsigned long long crt_ignerr; /* ignored verify errors in handshake if depth == 0 */
+	unsigned long long ca_ignerr_bitfield[IGNERR_BF_SIZE];   /* ignored verify errors in handshake if depth > 0 */
+	unsigned long long crt_ignerr_bitfield[IGNERR_BF_SIZE];  /* ignored verify errors in handshake if depth == 0 */
 	SSL_CTX *initial_ctx;      /* SSL context for initial negotiation */
 	SSL_CTX *default_ctx;      /* SSL context of first/default certificate */
 	struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h
index 974b836..35270ca 100644
--- a/include/haproxy/ssl_sock-t.h
+++ b/include/haproxy/ssl_sock-t.h
@@ -52,16 +52,20 @@
 #define SSL_SOCK_SEND_UNLIMITED     0x00000004
 #define SSL_SOCK_RECV_HEARTBEAT     0x00000008
 
-/* bits 0xFFFF0000 are reserved to store verify errors */
+/* bits 0xFFFFFF00 are reserved to store verify errors.
+ * The CA en CRT error codes will be stored on 7 bits each
+ * (since the max verify error code does not exceed 127)
+ * and the CA error depth will be stored on 4 bits.
+ */
 
 /* Verify errors macros */
-#define SSL_SOCK_CA_ERROR_TO_ST(e) (((e > 63) ? 63 : e) << (16))
-#define SSL_SOCK_CAEDEPTH_TO_ST(d) (((d > 15) ? 15 : d) << (6+16))
-#define SSL_SOCK_CRTERROR_TO_ST(e) (((e > 63) ? 63 : e) << (4+6+16))
+#define SSL_SOCK_CA_ERROR_TO_ST(e) (((e > 127) ? 127 : e) << (8))
+#define SSL_SOCK_CAEDEPTH_TO_ST(d) (((d > 15) ? 15 : d) << (7+8))
+#define SSL_SOCK_CRTERROR_TO_ST(e) (((e > 127) ? 127 : e) << (4+7+8))
 
-#define SSL_SOCK_ST_TO_CA_ERROR(s) ((s >> (16)) & 63)
-#define SSL_SOCK_ST_TO_CAEDEPTH(s) ((s >> (6+16)) & 15)
-#define SSL_SOCK_ST_TO_CRTERROR(s) ((s >> (4+6+16)) & 63)
+#define SSL_SOCK_ST_TO_CA_ERROR(s) ((s >> (8)) & 127)
+#define SSL_SOCK_ST_TO_CAEDEPTH(s) ((s >> (7+8)) & 15)
+#define SSL_SOCK_ST_TO_CRTERROR(s) ((s >> (4+7+8)) & 127)
 
 /* ssl_methods flags for ssl options */
 #define MC_SSL_O_ALL            0x0000
diff --git a/include/haproxy/ssl_sock.h b/include/haproxy/ssl_sock.h
index c68425a..498d4d6 100644
--- a/include/haproxy/ssl_sock.h
+++ b/include/haproxy/ssl_sock.h
@@ -149,6 +149,29 @@
 		return 1;
 }
 
+static inline int cert_ignerr_bitfield_get(const unsigned long long *bitfield, int bit_index)
+{
+	int byte_index = bit_index >> 6;
+	int val = 0;
+
+	if (byte_index < IGNERR_BF_SIZE)
+		val = bitfield[byte_index] & (1 << (bit_index & 0x3F));
+
+	return val != 0;
+}
+
+static inline void cert_ignerr_bitfield_set(unsigned long long *bitfield, int bit_index)
+{
+	int byte_index = bit_index >> 6;
+
+	if (byte_index < IGNERR_BF_SIZE)
+		bitfield[byte_index] |= (1 << (bit_index & 0x3F));
+}
+
+static inline void cert_ignerr_bitfield_set_all(unsigned long long *bitfield)
+{
+	memset(bitfield, -1, IGNERR_BF_SIZE*sizeof(*bitfield));
+}
 
 #endif /* USE_OPENSSL */
 #endif /* _HAPROXY_SSL_SOCK_H */
diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c
index 654b020..fcd0416 100644
--- a/src/cfgparse-ssl.c
+++ b/src/cfgparse-ssl.c
@@ -758,7 +758,7 @@
 {
 	int code;
 	char *p = args[cur_arg + 1];
-	unsigned long long *ignerr = &conf->crt_ignerr;
+	unsigned long long *ignerr = conf->crt_ignerr_bitfield;
 
 	if (!*p) {
 		memprintf(err, "'%s' : missing error IDs list", args[cur_arg]);
@@ -766,21 +766,21 @@
 	}
 
 	if (strcmp(args[cur_arg], "ca-ignore-err") == 0)
-		ignerr = &conf->ca_ignerr;
+		ignerr = conf->ca_ignerr_bitfield;
 
 	if (strcmp(p, "all") == 0) {
-		*ignerr = ~0ULL;
+		cert_ignerr_bitfield_set_all(ignerr);
 		return 0;
 	}
 
 	while (p) {
 		code = atoi(p);
-		if ((code <= 0) || (code > 63)) {
-			memprintf(err, "'%s' : ID '%d' out of range (1..63) in error IDs list '%s'",
-			          args[cur_arg], code, args[cur_arg + 1]);
+		if ((code <= 0) || (code > SSL_MAX_VFY_ERROR_CODE)) {
+			memprintf(err, "'%s' : ID '%d' out of range (1..%d) in error IDs list '%s'",
+			          args[cur_arg], code, SSL_MAX_VFY_ERROR_CODE, args[cur_arg + 1]);
 			return ERR_ALERT | ERR_FATAL;
 		}
-		*ignerr |= 1ULL << code;
+		cert_ignerr_bitfield_set(ignerr, code);
 		p = strchr(p, ',');
 		if (p)
 			p++;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index aeeb3c9..4480a04 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1626,7 +1626,8 @@
 			ctx->xprt_st |= SSL_SOCK_CAEDEPTH_TO_ST(depth);
 		}
 
-		if (err < 64 && __objt_listener(conn->target)->bind_conf->ca_ignerr & (1ULL << err)) {
+		if (err <= SSL_MAX_VFY_ERROR_CODE &&
+		    cert_ignerr_bitfield_get(__objt_listener(conn->target)->bind_conf->ca_ignerr_bitfield, err)) {
 			ssl_sock_dump_errors(conn);
 			ERR_clear_error();
 			return 1;
@@ -1640,7 +1641,8 @@
 		ctx->xprt_st |= SSL_SOCK_CRTERROR_TO_ST(err);
 
 	/* check if certificate error needs to be ignored */
-	if (err < 64 && __objt_listener(conn->target)->bind_conf->crt_ignerr & (1ULL << err)) {
+	if (err <= SSL_MAX_VFY_ERROR_CODE &&
+	    cert_ignerr_bitfield_get(__objt_listener(conn->target)->bind_conf->crt_ignerr_bitfield, err)) {
 		ssl_sock_dump_errors(conn);
 		ERR_clear_error();
 		return 1;