]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: {ca,crt}-ignore-err can now use error constant name
authorWilliam Lallemand <wlallemand@haproxy.org>
Thu, 3 Nov 2022 15:31:50 +0000 (16:31 +0100)
committerWilliam Lallemand <wlallemand@haproxy.org>
Thu, 10 Nov 2022 12:28:37 +0000 (13:28 +0100)
The ca-ignore-err and crt-ignore-err directives are now able to use the
openssl X509_V_ERR constant names instead of the numerical values.

This allow a configuration to survive an OpenSSL upgrade, because the
numerical ID can change between versions. For example
X509_V_ERR_INVALID_CA was 24 in OpenSSL 1 and is 79 in OpenSSL 3.

The list of errors must be updated when a new major OpenSSL version is
released.

doc/configuration.txt
include/haproxy/ssl_utils.h
reg-tests/ssl/ssl_client_auth.vtc
src/cfgparse-ssl.c
src/ssl_utils.c

index d3ab65cc8157e22b9332303097fc5c9f3a77bb23..5d617d74af4dc044d6b98cbd062b4354cca6a434 100644 (file)
@@ -14084,6 +14084,11 @@ ca-file <cafile>
 ca-ignore-err [all|<errorID>,...]
   This setting is only available when support for OpenSSL was built in.
   Sets a comma separated list of errorIDs to ignore during verify at depth > 0.
+  It could be a numerical ID, or the constant name (X509_V_ERR) which is
+  available in the OpenSSL documentation:
+  https://www.openssl.org/docs/manmaster/man3/X509_STORE_CTX_get_error.html#ERROR-CODES
+  It is recommended to use the constant name as the numerical value can change
+  in new version of OpenSSL.
   If set to 'all', all errors are ignored. SSL handshake is not aborted if an
   error is ignored.
 
@@ -14204,9 +14209,14 @@ crt <cert>
 
 crt-ignore-err <errors>
   This setting is only available when support for OpenSSL was built in. Sets a
-  comma separated list of errorIDs to ignore during verify at depth == 0. If
-  set to 'all', all errors are ignored. SSL handshake is not aborted if an error
-  is ignored.
+  comma separated list of errorIDs to ignore during verify at depth == 0.
+  It could be a numerical ID, or the constant name (X509_V_ERR) which is
+  available in the OpenSSL documentation:
+  https://www.openssl.org/docs/manmaster/man3/X509_STORE_CTX_get_error.html#ERROR-CODES
+  It is recommended to use the constant name as the numerical value can change
+  in new version of OpenSSL.
+  If set to 'all', all errors are ignored. SSL handshake is not aborted if an
+  error is ignored.
 
 crt-list <file>
   This setting is only available when support for OpenSSL was built in. It
index e14aaf1c6406b018250487375a44dea93eb74afd..b6bd9d6a4654f86d1cb09f103337447006f5e1e9 100644 (file)
@@ -41,6 +41,8 @@ int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out);
 X509* ssl_sock_get_peer_certificate(SSL *ssl);
 unsigned int openssl_version_parser(const char *version);
 void exclude_tls_grease(char *input, int len, struct buffer *output);
+int x509_v_err_str_to_int(const char *str);
+const char *x509_v_err_int_to_str(int code);
 
 #endif /* _HAPROXY_SSL_UTILS_H */
 #endif /* USE_OPENSSL */
index 0278ec0eaddb14f98ca300a3d7f93a6d004038bc..b8107d4434b1252056d78486d3ad669ae52ffb60 100644 (file)
@@ -48,7 +48,7 @@ haproxy h1 -conf {
         # crt: certificate of the server
         # ca-file: CA used for client authentication request
         # crl-file: revocation list for client auth: the client1 certificate is revoked
-        bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err all crl-file ${testdir}/crl-auth.pem
+        bind "${tmpdir}/ssl.sock" ssl crt ${testdir}/common.pem ca-file ${testdir}/ca-auth.crt verify optional crt-ignore-err X509_V_ERR_CERT_REVOKED,X509_V_ERR_CERT_HAS_EXPIRED crl-file ${testdir}/crl-auth.pem
 
         acl cert_expired ssl_c_verify 10
         acl cert_revoked ssl_c_verify 23
index 35780adff4f34602a3ec31b1c811fc2d9b2f9266..7cee13db039d384ec5ece9500241cc2dfe910f49 100644 (file)
@@ -37,6 +37,7 @@
 #include <haproxy/listener.h>
 #include <haproxy/openssl-compat.h>
 #include <haproxy/ssl_sock.h>
+#include <haproxy/ssl_utils.h>
 #include <haproxy/tools.h>
 #include <haproxy/ssl_ckch.h>
 
@@ -824,7 +825,10 @@ static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct b
 static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
        int code;
+       char *s1 = NULL, *s2 = NULL;
+       char *token = NULL;
        char *p = args[cur_arg + 1];
+       char *str;
        unsigned long long *ignerr = conf->crt_ignerr_bitfield;
 
        if (!*p) {
@@ -832,6 +836,15 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
                return ERR_ALERT | ERR_FATAL;
        }
 
+       /* copy the string to be able to dump the complete one in case of
+        * error, because strtok_r is writing \0 inside. */
+       str = strdup(p);
+       if (!str) {
+               memprintf(err, "'%s' : Could not allocate memory", args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+
+       }
+
        if (strcmp(args[cur_arg], "ca-ignore-err") == 0)
                ignerr = conf->ca_ignerr_bitfield;
 
@@ -840,19 +853,31 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
                return 0;
        }
 
-       while (p) {
-               code = atoi(p);
-               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;
+       s1 = str;
+       while ((token = strtok_r(s1, ",", &s2))) {
+               s1 = NULL;
+               printf("token: %s\n", token);
+               if (isdigit((int)*token)) {
+                       code = atoi(token);
+                       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]);
+                               free(str);
+                               return ERR_ALERT | ERR_FATAL;
+                       }
+               } else {
+                       code = x509_v_err_str_to_int(token);
+                       if (code < 0) {
+                               memprintf(err, "'%s' : error constant '%s' unknown in error IDs list '%s'",
+                                         args[cur_arg], token, args[cur_arg + 1]);
+                               free(str);
+                               return ERR_ALERT | ERR_FATAL;
+                       }
                }
                cert_ignerr_bitfield_set(ignerr, code);
-               p = strchr(p, ',');
-               if (p)
-                       p++;
        }
 
+       free(str);
        return 0;
 }
 
index 0f4a8596c9f18839727f8123771b403b8f6cadf6..f832168bd804afdb0a2148ab4c9a680a916f3f7d 100644 (file)
@@ -417,3 +417,191 @@ void exclude_tls_grease(char *input, int len, struct buffer *output)
        if (output->size - output->data > 0 && len - ptr > 0)
                output->area[output->data++] = input[ptr];
 }
+
+/*
+ * The following generates an array <x509_v_codes> in which the X509_V_ERR_*
+ * codes are populated with there string equivalent. Depending on the version
+ * of the SSL library, some code does not exist, these will be populated as
+ * "-1" in the array.
+ *
+ * The list was taken from
+ * https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
+ * and must be updated when new constant are introduced.
+ */
+
+/* manual atoi() that only works on the first 10 chars of input (they must all be there) */
+#undef _S
+#define _S(x) ((x[0]-'0')*1000000000 +                  \
+               (x[1]-'0')*100000000 +                   \
+               (x[2]-'0')*10000000 +                    \
+               (x[3]-'0')*1000000 +                     \
+               (x[4]-'0')*100000 +                      \
+               (x[5]-'0')*10000 +                       \
+               (x[6]-'0')*1000 +                        \
+               (x[7]-'0')*100 +                         \
+               (x[8]-'0')*10 +                          \
+               (x[9]-'0')*1 +                           \
+               0)
+
+/* always prepends the sufficient number of leading zeroes to have 10 chars */
+#undef _R
+#define _R(x) (!x[0] ? _S("0000000000" x) :       \
+               !x[1] ? _S("000000000"  x) :       \
+               !x[2] ? _S("00000000"   x) :       \
+               !x[3] ? _S("0000000"    x) :       \
+               !x[4] ? _S("000000"     x) :       \
+               !x[5] ? _S("00000"      x) :       \
+               !x[6] ? _S("0000"       x) :       \
+               !x[7] ? _S("000"        x) :       \
+               !x[8] ? _S("00"         x) :       \
+               !x[9] ? _S("0"          x) :       \
+                       _S(""           x))
+
+/* returns the value for an integer-defined macro, otherwise -1
+ * The extraneous series of "\0" is there to shut up stupid clang which wants to
+ * evaluate the expression in false branches.
+ */
+#undef _Q
+#define _Q(x) ((#x[0] >= '0' && #x[0] <= '9') ? _R(#x "\0\0\0\0\0\0\0\0\0\0") : -1)
+#undef V
+#define V(x) { .code = _Q(x), .string = #x }
+
+static const struct x509_v_codes {
+       int code;
+       const char *string;
+} x509_v_codes[] = {
+       V(X509_V_OK),
+       V(X509_V_ERR_UNSPECIFIED),
+       V(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT),
+       V(X509_V_ERR_UNABLE_TO_GET_CRL),
+       V(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE),
+       V(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE),
+       V(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY),
+       V(X509_V_ERR_CERT_SIGNATURE_FAILURE),
+       V(X509_V_ERR_CRL_SIGNATURE_FAILURE),
+       V(X509_V_ERR_CERT_NOT_YET_VALID),
+       V(X509_V_ERR_CERT_HAS_EXPIRED),
+       V(X509_V_ERR_CRL_NOT_YET_VALID),
+       V(X509_V_ERR_CRL_HAS_EXPIRED),
+       V(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD),
+       V(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD),
+       V(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD),
+       V(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD),
+       V(X509_V_ERR_OUT_OF_MEM),
+       V(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT),
+       V(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN),
+       V(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY),
+       V(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE),
+       V(X509_V_ERR_CERT_CHAIN_TOO_LONG),
+       V(X509_V_ERR_CERT_REVOKED),
+       V(X509_V_ERR_NO_ISSUER_PUBLIC_KEY),
+       V(X509_V_ERR_PATH_LENGTH_EXCEEDED),
+       V(X509_V_ERR_INVALID_PURPOSE),
+       V(X509_V_ERR_CERT_UNTRUSTED),
+       V(X509_V_ERR_CERT_REJECTED),
+       V(X509_V_ERR_SUBJECT_ISSUER_MISMATCH),
+       V(X509_V_ERR_AKID_SKID_MISMATCH),
+       V(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH),
+       V(X509_V_ERR_KEYUSAGE_NO_CERTSIGN),
+       V(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER),
+       V(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION),
+       V(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN),
+       V(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION),
+       V(X509_V_ERR_INVALID_NON_CA),
+       V(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED),
+       V(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE),
+       V(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED),
+       V(X509_V_ERR_INVALID_EXTENSION),
+       V(X509_V_ERR_INVALID_POLICY_EXTENSION),
+       V(X509_V_ERR_NO_EXPLICIT_POLICY),
+       V(X509_V_ERR_DIFFERENT_CRL_SCOPE),
+       V(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE),
+       V(X509_V_ERR_UNNESTED_RESOURCE),
+       V(X509_V_ERR_PERMITTED_VIOLATION),
+       V(X509_V_ERR_EXCLUDED_VIOLATION),
+       V(X509_V_ERR_SUBTREE_MINMAX),
+       V(X509_V_ERR_APPLICATION_VERIFICATION),
+       V(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE),
+       V(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX),
+       V(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX),
+       V(X509_V_ERR_CRL_PATH_VALIDATION_ERROR),
+       V(X509_V_ERR_PATH_LOOP),
+       V(X509_V_ERR_SUITE_B_INVALID_VERSION),
+       V(X509_V_ERR_SUITE_B_INVALID_ALGORITHM),
+       V(X509_V_ERR_SUITE_B_INVALID_CURVE),
+       V(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM),
+       V(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED),
+       V(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256),
+       V(X509_V_ERR_HOSTNAME_MISMATCH),
+       V(X509_V_ERR_EMAIL_MISMATCH),
+       V(X509_V_ERR_IP_ADDRESS_MISMATCH),
+       V(X509_V_ERR_DANE_NO_MATCH),
+       V(X509_V_ERR_EE_KEY_TOO_SMALL),
+       V(X509_V_ERR_CA_KEY_TOO_SMALL),
+       V(X509_V_ERR_CA_MD_TOO_WEAK),
+       V(X509_V_ERR_INVALID_CALL),
+       V(X509_V_ERR_STORE_LOOKUP),
+       V(X509_V_ERR_NO_VALID_SCTS),
+       V(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION),
+       V(X509_V_ERR_OCSP_VERIFY_NEEDED),
+       V(X509_V_ERR_OCSP_VERIFY_FAILED),
+       V(X509_V_ERR_OCSP_CERT_UNKNOWN),
+       V(X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM),
+       V(X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH),
+       V(X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY),
+       V(X509_V_ERR_INVALID_CA),
+       V(X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA),
+       V(X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN),
+       V(X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA),
+       V(X509_V_ERR_ISSUER_NAME_EMPTY),
+       V(X509_V_ERR_SUBJECT_NAME_EMPTY),
+       V(X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER),
+       V(X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER),
+       V(X509_V_ERR_EMPTY_SUBJECT_ALT_NAME),
+       V(X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL),
+       V(X509_V_ERR_CA_BCONS_NOT_CRITICAL),
+       V(X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL),
+       V(X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL),
+       V(X509_V_ERR_CA_CERT_MISSING_KEY_USAGE),
+       V(X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3),
+       V(X509_V_ERR_EC_KEY_EXPLICIT_PARAMS),
+       { 0, NULL },
+};
+
+/*
+ * Return the X509_V_ERR code corresponding to the name of the constant.
+ * See https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
+ * If not found, return -1
+ */
+int x509_v_err_str_to_int(const char *str)
+{
+       int i;
+
+       for (i = 0; x509_v_codes[i].string; i++) {
+               if (strcmp(str, x509_v_codes[i].string) == 0) {
+                       return x509_v_codes[i].code;
+               }
+       }
+
+       return -1;
+}
+
+/*
+ * Return the constant name corresponding to the X509_V_ERR code
+ * See https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
+ * If not found, return NULL;
+ */
+const char *x509_v_err_int_to_str(int code)
+{
+       int i;
+
+       if (code == -1)
+               return NULL;
+
+       for (i = 0; x509_v_codes[i].string; i++) {
+               if (x509_v_codes[i].code == code) {
+                       return x509_v_codes[i].string;
+               }
+       }
+       return NULL;
+}